Index: admin/bloglevelupgrade.php
=========================================================
--- admin/bloglevelupgrade.php	(revision 0)
+++ admin/bloglevelupgrade.php	Mon Oct 19 14:40:30 WST 2009
@@ -0,0 +1,154 @@
+<?php /// $Id: multilangupgrade.php,v 1.15 2009/08/20 08:39:07 nicolasconnault Exp $
+      /// Create "blog" forums in each course and copy blog entries from these courses' participants in these forums
+
+require_once('../config.php');
+require_once($CFG->dirroot.'/course/lib.php');
+require_once($CFG->dirroot.'/blog/lib.php');
+require_once($CFG->dirroot.'/mod/forum/lib.php');
+require_once($CFG->libdir.'/adminlib.php');
+
+admin_externalpage_setup('bloglevelupgrade');
+$PAGE->set_generaltype('maintenance');
+
+$go = optional_param('go', 0, PARAM_BOOL);
+
+admin_externalpage_print_header();
+echo $OUTPUT->heading(get_string('bloglevelupgrade', 'admin'));
+
+$strbloglevelupgrade = get_string('bloglevelupgradeinfo', 'admin');
+
+if (!$go or !data_submitted() or !confirm_sesskey()) {   /// Print a form
+    $optionsyes = array('go'=>1, 'sesskey'=>sesskey());
+    echo $OUTPUT->confirm($strbloglevelupgrade, new moodle_url('bloglevelupgrade.php', $optionsyes), 'index.php');
+    echo $OUTPUT->footer();
+    die;
+}
+
+echo $OUTPUT->box_start();
+
+/// Turn off time limits, sometimes upgrades can be slow.
+
+@set_time_limit(0);
+@ob_implicit_flush(true);
+while(@ob_end_flush());
+
+$i = 0;
+
+// If $CFG->bloglevel is set to BLOG_GROUP_LEVEL or BLOG_COURSE_LEVEL, create a new "blog" forum in each course
+// whose enrolled students have written blog entries, copy these entries in that forum and switch off blogs at site level
+
+if ($CFG->bloglevel == BLOG_COURSE_LEVEL || $CFG->bloglevel == BLOG_GROUP_LEVEL) {
+    $pbar = new progress_bar('bloglevelupgrade', 500, true);
+
+    $bloggers = $DB->get_records_sql("SELECT userid FROM {post} WHERE module = 'blog' GROUP BY userid");
+    require_once($CFG->dirroot.'/mod/forum/lib.php');
+
+    $realuser = clone($USER);
+    $a = new object();
+    $a->userscount = 0;
+    $a->blogcount = 0;
+
+    foreach ($bloggers as $blogger) {
+        $courses = get_my_courses($blogger->userid);
+        $USER->id = $blogger->userid;
+        $blogentries = $DB->get_records('post', array('module' => 'blog', 'userid' => $blogger->userid));
+
+        foreach ($courses as $course) {
+            $forum = forum_get_course_forum($course->id, 'blog');
+            $cm = get_coursemodule_from_instance('forum', $forum->id);
+
+            if ($CFG->bloglevel == BLOG_GROUP_LEVEL && $course->groupmode != NOGROUPS) {
+                // Unless the course is set to separate groups forced, force the forum to Separate groups
+                if (!($course->groupmode == SEPARATEGROUPS && $course->groupmodeforce)) {
+                    $cm->groupmode = SEPARATEGROUPS;
+                    $DB->update_record('course_modules', $cm);
+                }
+
+                $groups = groups_get_user_groups($course->id, $blogger->userid);
+                foreach ($groups[0] as $groupid) { // [0] is for all groupings combined
+                    $a->blogcount += bloglevelupgrade_entries($blogentries, $forum, $cm, $groupid);
+                }
+            } else {
+                $a->blogcount += bloglevelupgrade_entries($blogentries, $forum, $cm);
+            }
+        }
+
+        $a->userscount = $i . '/' .  count($bloggers);
+        $pbar->update($i, count($bloggers), get_string('bloglevelupgradeprogress', 'admin', $a));
+        $i++;
+    }
+
+    $USER = clone($realuser);
+}
+
+function bloglevelupgrade_entries($blogentries, $forum, $cm, $groupid=-1) {
+    $count = 0;
+
+    $forumcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
+    $sitecontext = get_context_instance(CONTEXT_SYSTEM);
+
+    foreach ($blogentries as $blogentry) {
+        $discussion = new stdClass();
+        $discussion->course = $forum->course;
+        $discussion->forum = $forum->id;
+        $discussion->name = $blogentry->subject;
+        $discussion->intro = $blogentry->summary;
+        $discussion->assessed = $forum->assessed;
+        $discussion->messageformat = $forum->introformat;
+        $discussion->messagetrust = 0;
+        $discussion->attachments = 0;
+        $discussion->mailnow = false;
+        $discussion->timemodified = $blogentry->created;
+        $discussion->itemid = null;
+        $discussion->groupid = $groupid;
+        $message = '';
+
+        $discussionid = forum_add_discussion($discussion, null, $message);
+
+        // Copy file attachment records
+        $fs = get_file_storage();
+        $files = $fs->get_area_files($sitecontext->id, 'blog_attachment', $blogentry->id);
+
+        if (!empty($files)) {
+            foreach ($files as $storedfile) {
+                $newfile = new object();
+                $newfile->filearea = 'forum_attachment';
+                $newfile->itemid = $discussion->firstpost;
+                $newfile->contextid = $forumcontext->id;
+                $fs->create_file_from_storedfile($newfile, $storedfile->get_id());
+            }
+        }
+
+        $files = $fs->get_area_files($sitecontext->id, 'blog_post', $blogentry->id);
+
+        if (!empty($files)) {
+            foreach ($files as $storedfile) {
+                $newfile = new object();
+                $newfile->filearea = 'forum_post';
+                $newfile->itemid = $discussion->firstpost;
+                $newfile->contextid = $forumcontext->id;
+                $fs->create_file_from_storedfile($newfile, $storedfile->get_id());
+            }
+        }
+        $count++;
+    }
+    return $count;
+}
+// END OF LOOP
+
+// set conversion flag - switches to new plugin automatically
+set_config('bloglevel_upgrade_complete', 1);
+// Finally switch bloglevel to 0 (disabled)
+set_config('bloglevel', 0);
+
+echo $OUTPUT->box_end();
+
+/// Rebuild course cache which might be incorrect now
+echo $OUTPUT->notification('Rebuilding course cache...', 'notifysuccess');
+rebuild_course_cache();
+echo $OUTPUT->notification('...finished', 'notifysuccess');
+
+echo $OUTPUT->continue_button('index.php');
+
+echo $OUTPUT->footer();
+die;
Index: admin/index.php
=========================================================
--- admin/index.php	(revision 1.406)
+++ admin/index.php	Thu Oct 15 09:39:03 WST 2009
@@ -385,6 +385,12 @@
     echo $OUTPUT->box(get_string('multilangupgradenotice', 'admin'), 'generalbox adminwarning');
 }
 
+// Hidden bloglevel upgrade
+$showbloglevelupgrade = ($CFG->bloglevel == BLOG_COURSE_LEVEL || $CFG->bloglevel == BLOG_GROUP_LEVEL) && empty($CFG->bloglevel_upgrade_complete);
+if ($showbloglevelupgrade) {
+    echo $OUTPUT->box(get_string('bloglevelupgradenotice', 'admin'), 'generalbox adminwarning');
+}
+
 // Alert if we are currently in maintenance mode
 if (!empty($CFG->maintenance_enabled)) {
     echo $OUTPUT->box(get_string('sitemaintenancewarning2', 'admin', "$CFG->wwwroot/$CFG->admin/settings.php?section=maintenancemode"), 'generalbox adminwarning');
Index: blocks/blog_menu/block_blog_menu.php
=========================================================
--- blocks/blog_menu/block_blog_menu.php	(revision 1.29)
+++ blocks/blog_menu/block_blog_menu.php	Thu Oct 29 10:12:38 WST 2009
@@ -70,21 +70,7 @@
         $this->content = new stdClass;
         $this->content->footer = '';
 
-        $viewblogentriesurl = blog_get_context_url();
-        $strlevel = '';
-
-        switch ($context->contextlevel) {
-            case CONTEXT_COURSE:
-                $strlevel = ($context->instanceid == SITEID) ? '' : get_string('course');
-                break;
-            case CONTEXT_MODULE:
-                $strlevel = print_context_name($context);
-                break;
-            case CONTEXT_USER:
-                $strlevel = get_string('user');
-                break;
-        }
-
+        $blogheaders = blog_get_headers();
         $canviewblogs = has_capability('moodle/blog:view', $context);
 
         /// Accessibility: markup as a list.
@@ -93,16 +79,19 @@
         $menulist = new html_list();
         $menulist->add_class('list');
 
-        if (!empty($strlevel)) {
-            $url = html_link::make($viewblogentriesurl, get_string('viewblogentries', 'blog', $strlevel));
-            $url->disableifcurrent = true;
+        if (!empty($blogheaders['strview']) && $CFG->useblogassociations) {
+            $url = html_link::make($blogheaders['url'], $blogheaders['strview']);
+            if ($blogheaders['url']->compare($PAGE->url)) {
+                $url->disabled = true;
+            }
             $menulist->add_item($OUTPUT->link($url));
         }
 
         // show View site entries link
         if ($CFG->bloglevel >= BLOG_SITE_LEVEL && $canviewblogs) {
             $viewsiteentriesurl = html_link::make($CFG->wwwroot .'/blog/index.php', get_string('viewsiteentries', 'blog'));
-            if (!$PAGE->url->param('search') && !$PAGE->url->param('tag') && !$PAGE->url->param('tagid')) {
+            if (!$PAGE->url->param('search') && !$PAGE->url->param('tag') && !$PAGE->url->param('tagid') &&
+                !$PAGE->url->param('modid') && !$PAGE->url->param('courseid') && !$PAGE->url->param('userid') && !$PAGE->url->param('entryid')) {
                 $viewsiteentriesurl->disableifcurrent = true;
             }
             $menulist->add_item($OUTPUT->link($viewsiteentriesurl));
@@ -111,30 +100,34 @@
         $output .= '';
 
         // show View my entries link
-        if ($context->contextlevel != CONTEXT_USER) {
-            $myentrieslink = html_link::make(new moodle_url($CFG->wwwroot .'/blog/index.php', array('userid' => $USER->id)), get_string('viewmyentries', 'blog'));
+        $myentrieslink = html_link::make(new moodle_url($CFG->wwwroot .'/blog/index.php', array('userid' => $USER->id)), get_string('viewmyentries', 'blog'));
-            $myentrieslink->url->params($viewblogentriesurl->params());
-            $myentrieslink->disableifcurrent = true;
+        $myentrieslink->url->params($blogheaders['url']->params());
+        $pageuserid = $PAGE->url->param('userid');
+        if (!empty($pageuserid) && $pageuserid == $USER->id) {
+            $myentrieslink->disabled = true;
+        }
+
-            $menulist->add_item($OUTPUT->link($myentrieslink));
+        $menulist->add_item($OUTPUT->link($myentrieslink));
-        }
 
-        // show Add entry link
+        // show "Add entry" or "Blog about this" link
         $sitecontext = get_context_instance(CONTEXT_SYSTEM);
         if (has_capability('moodle/blog:create', $sitecontext)) {
-            $addentrylink = html_link::make(new moodle_url($CFG->wwwroot .'/blog/edit.php', array('action' => 'add')), get_string('addnewentry', 'blog'));
-            $addentrylink->url->params($viewblogentriesurl->params());
+            $addentrylink = html_link::make(new moodle_url($CFG->wwwroot .'/blog/edit.php', array('action' => 'add')), $blogheaders['stradd']);
+            $addentrylink->url->params($blogheaders['url']->params());
             $addentrylink->disableifcurrent = true;
             $menulist->add_item($OUTPUT->link($addentrylink));
         }
 
         // Full-text search field
+        if (has_capability('moodle/blog:search', $sitecontext)) {
-        $searchform = new html_form();
-        $searchform->method = 'get';
+            $searchform = new html_form();
+            $searchform->method = 'get';
-        $searchform->url = new moodle_url($viewblogentriesurl);
+            $searchform->url = new moodle_url($blogheaders['url']);
-        $searchform->button->text = get_string('search');
-        $formcontents = $OUTPUT->field(html_field::make_text('search', '', '', 99));
+            $searchform->button->text = get_string('search');
+            $formcontents = $OUTPUT->field(html_field::make_text('search', '', '', 99));
+            $menulist->add_item($OUTPUT->form($searchform, $formcontents));
+        }
 
-        $menulist->add_item($OUTPUT->form($searchform, $formcontents));
         $this->content->text = $OUTPUT->htmllist($menulist);
     }
 }
Index: blocks/blog_recent/block_blog_recent.php
=========================================================
--- blocks/blog_recent/block_blog_recent.php	(revision 1.1)
+++ blocks/blog_recent/block_blog_recent.php	Fri Oct 16 15:17:35 WST 2009
@@ -40,73 +40,63 @@
         $this->version = 2009070900;
     }
 
-    function get_content() {
-        global $CFG, $USER, $PAGE, $DB;
+    function has_config() {
+        return true;
+    }
 
-        $this->content = new stdClass();
-        $this->content->footer = '';
+    function instance_allow_config() {
+        return true;
+    }
 
-        $tag     = optional_param('tag', null, PARAM_NOTAGS);
-        $tagid   = optional_param('tagid', null, PARAM_INT);
-        $entryid = optional_param('entryid', null, PARAM_INT);
-        $groupid = optional_param('groupid', null, PARAM_INT);
-        $search  = optional_param('search', null, PARAM_RAW);
+    function get_content() {
+        global $CFG, $USER, $PAGE, $DB, $OUTPUT;
 
-        //correct tagid if a text tag is provided as a param
-        if (!empty($tag)) {  //text tag parameter takes precedence
-            if ($tagrec = $DB->get_record_sql("SELECT * FROM {tag} WHERE name LIKE ?", array($tag))) {
-                $tagid = $tagrec->id;
-            } else {
-                unset($tagid);
+        if (empty($CFG->bloglevel) || ($CFG->bloglevel < BLOG_GLOBAL_LEVEL && !(isloggedin() && !isguestuser()))) {
+            $this->content->text = '';
+            if ($this->page->user_is_editing()) {
+                $this->content->text = get_string('blogdisable', 'blog');
             }
+            return $this->content;
         }
 
+        $this->content = new stdClass();
+        $this->content->footer = '';
+
         $context = $PAGE->get_context();
-        
+
-        $strlevel = '';
+        $blogheaders = blog_get_headers();
 
-        switch ($context->contextlevel) {
-            case CONTEXT_COURSE:
-                $strlevel = ($context->instanceid == SITEID) ? '' : get_string('course');
-                break;
-            case CONTEXT_MODULE:
-                $strlevel = print_context_name($context);
-                break;
-            case CONTEXT_USER:
-                $strlevel = get_string('user');
-                break;
+        // Remove entryid filter
+        if (!empty($blogheaders['filters']['entry'])) {
+            unset($blogheaders['filters']['entry']);
+            $blogheaders['url']->remove_params(array('entryid'));
         }
 
-        $filters = array();
+        $blogheaders['filters']['since'] = $this->config->recentbloginterval;
 
-        if (!empty($entryid)) {
-            $filters['entry'] = $entryid;
-        }
+        $bloglisting = new blog_listing($blogheaders['filters']);
+        $entries = $bloglisting->get_entries(0, $this->config->numberofrecentblogentries, 4);
 
-        if (!empty($groupid)) {
-            $filters['group'] = $groupid;
-        }
+        if (!empty($entries)) {
+            $entrieslist = new html_list();
+            $entrieslist->add_class('list');
+            $viewblogurl = new moodle_url($CFG->wwwroot . '/blog/index.php');
 
-        if (!empty($tagid)) {
-            $filters['tag'] = $tagid;
+            foreach ($entries as $entryid => $entry) {
+                $viewblogurl->param('entryid', $entryid);
+                $entrylink = html_link::make($viewblogurl, shorten_text($entry->subject));
+                $entrieslist->add_item($OUTPUT->link($entrylink));
-        }
+            }
 
-        if (!empty($search)) {
-            $filters['search'] = $search;
+            $this->content->text .= $OUTPUT->htmllist($entrieslist);
+            $strview = get_string('viewsiteentries', 'blog');
+            if (!empty($blogheaders['strview'])) {
+                $strview = $blogheaders['strview'];
-        }
+            }
-
-        $blog_listing = new blog_listing($filters);
-        $entries = $blog_listing->get_entries(0, get_user_preferences('blogrecententriesnumber', 4));
-
-        $this->content->text = '<ul class="list">';
-        $viewblog_url = $CFG->wwwroot . '/blog/index.php?entryid=';
-
-        foreach ($entries as $entry_id => $entry) {
-            $this->content->text .= "<li><a href=\"$viewblog_url$entry_id\">".shorten_text($entry->subject)."</a></li>\n";
+            $viewallentrieslink = html_link::make($blogheaders['url'], $strview);
+            $this->content->text .= $OUTPUT->link($viewallentrieslink);
+        } else {
+            $this->content->text .= get_string('norecentblogentries', 'block_blog_recent');
         }
-
-        $this->content->text .= '<li>&nbsp;</li>';
-        $this->content->text .= '<li><a href="'.blog_get_context_url().'">'.get_string('viewallblogentries', 'blog', $strlevel).'</a></li>'; 
-        $this->content->text .= '</ul>';
     }
 }
Index: blocks/blog_recent/edit_form.php
=========================================================
--- blocks/blog_recent/edit_form.php	(revision 0)
+++ blocks/blog_recent/edit_form.php	Thu Oct 15 11:30:05 WST 2009
@@ -0,0 +1,59 @@
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Form for editing tag block instances.
+ *
+ * @package   moodlecore
+ * @copyright 2009 Tim Hunt
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * Form for editing tag block instances.
+ *
+ * @copyright 2009 Tim Hunt
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class block_blog_recent_edit_form extends block_edit_form {
+    protected function specific_definition($mform) {
+        // Fields for editing HTML block title and contents.
+        $mform->addElement('header', 'configheader', get_string('blocksettings', 'block'));
+
+        $numberofentries = array();
+        for ($i = 1; $i <= 20; $i++) {
+            $numberofentries[$i] = $i;
+        }
+
+        $mform->addElement('select', 'config_numberofrecentblogentries', get_string('numentriestodisplay', 'block_blog_recent'), $numberofentries);
+        $mform->setDefault('config_numberofrecentblogentries', 4);
+
+
+        $intervals = array(
+                7200 => get_string('numhours', '', 2),
+                14400 => get_string('numhours', '', 4),
+                21600 => get_string('numhours', '', 6),
+                43200 => get_string('numhours', '', 12),
+                86400 => get_string('numhours', '', 24),
+                172800 => get_string('numdays', '', 2),
+                604800 => get_string('numdays', '', 7)
+                );
+
+        $mform->addElement('select', 'config_recentbloginterval', get_string('recentinterval', 'block_blog_recent'), $intervals);
+        $mform->setDefault('config_recentbloginterval', 86400);
+    }
+}
Index: blocks/blog_tags/block_blog_tags.php
=========================================================
--- blocks/blog_tags/block_blog_tags.php	(revision 1.43)
+++ blocks/blog_tags/block_blog_tags.php	Fri Oct 23 11:37:31 WST 2009
@@ -43,6 +43,9 @@
 
         if (empty($CFG->usetags) || empty($CFG->bloglevel)) {
             $this->content->text = '';
+            if ($this->page->user_is_editing()) {
+                $this->content->text = get_string('tagsaredisabled', 'tag');
+            }
             return $this->content;
         }
 
@@ -64,9 +67,10 @@
         $this->content->text = '';
         $this->content->footer = '';
 
-        /// Get a list of tags
+        /// Get a list of tags 
+        $timewithin = time() - $this->config->timewithin * 24 * 60 * 60; /// convert to seconds
-
+        
-        $timewithin = time() - $this->config->timewithin * 24 * 60 * 60; /// convert to seconds
+        $blogheaders = blog_get_headers();
 
         // admins should be able to read all tags
         $type = '';
@@ -75,11 +79,21 @@
         }
 
         $sql  = "SELECT t.id, t.tagtype, t.rawname, t.name, COUNT(DISTINCT ti.id) AS ct
-                   FROM {tag} t, {tag_instance} ti, {post} p
+                   FROM {tag} t, {tag_instance} ti, {post} p, {blog_association} ba
                   WHERE t.id = ti.tagid AND p.id = ti.itemid
                         $type
                         AND ti.itemtype = 'post'
-                        AND ti.timemodified > $timewithin
+                        AND ti.timemodified > $timewithin";
+
+        if (!empty($blogheaders['filters']['module'])) {
+            $modulecontext = get_context_instance(CONTEXT_MODULE, $blogheaders['filters']['module']);
+            $sql .= " AND ba.contextid = $modulecontext->id AND p.id = ba.blogid ";
+        } else if (!empty($blogheaders['filters']['course'])) {
+            $coursecontext = get_context_instance(CONTEXT_COURSE, $blogheaders['filters']['course']);
+            $sql .= " AND ba.contextid = $coursecontext->id AND p.id = ba.blogid ";
+        }
+
+        $sql .= "
                GROUP BY t.id, t.tagtype, t.name, t.rawname
                ORDER BY ct DESC, t.name ASC";
 
@@ -122,24 +136,25 @@
         /// Accessibility: markup as a list.
             $this->content->text .= "\n<ul class='inline-list'>\n";
             foreach ($etags as $tag) {
+                $blogurl = new moodle_url($CFG->wwwroot.'/blog/index.php');
+
                 switch ($CFG->bloglevel) {
                     case BLOG_USER_LEVEL:
-                        $filtertype = 'user';
-                        $filterselect = $USER->id;
+                        $blogurl->param('userid', $USER->id);
                     break;
 
                     default:
-                        if ($this->page->course->id != SITEID) {
-                            $filtertype = 'course';
-                            $filterselect = $this->page->course->id;
-                        } else {
-                            $filtertype = 'site';
-                            $filterselect = SITEID;
+                        if (!empty($blogheaders['filters']['module'])) {
+                            $blogurl->param('modid', $blogheaders['filters']['module']);
+                        } else if (!empty($blogheaders['filters']['course'])) {
+                            $blogurl->param('courseid', $blogheaders['filters']['course']);
                         }
+                        
                     break;
                 }
 
-                $link = html_link::make(blog_get_blogs_url(array($filtertype => $filterselect, 'tag'=>$tag->id)), tag_display_name($tag));
+                $blogurl->param('tagid', $tag->id);
+                $link = html_link::make($blogurl, tag_display_name($tag));
                 $link->add_class($tag->class);
                 $link->title = get_string('numberofentries','blog',$tag->ct);
                 $this->content->text .= '<li>' . $OUTPUT->link($link) . '</li> ';
Index: blog/edit.php
=========================================================
--- blog/edit.php	(revision 1.82)
+++ blog/edit.php	Thu Oct 29 10:00:21 WST 2009
@@ -27,17 +27,36 @@
 require_once(dirname(dirname(__FILE__)).'/config.php');
 include_once('lib.php');
 include_once('locallib.php');
-include_once($CFG->dirroot.'/tag/lib.php');
 
 $action   = required_param('action', PARAM_ALPHA);
 $id       = optional_param('entryid', 0, PARAM_INT);
 $confirm  = optional_param('confirm', 0, PARAM_BOOL);
-$modid    = optional_param('modid', 0, PARAM_INT);
-$courseid = optional_param('courseid', 0, PARAM_INT); // needed for user tab - does nothing here
+$modid    = optional_param('modid', 0, PARAM_INT); // To associate the entry with a module instance
+$courseid = optional_param('courseid', 0, PARAM_INT); // To associate the entry with a course
 
 $PAGE->set_url('blog/edit.php', array('action' => $action, 'entryid' => $id, 'confirm' => $confirm, 'modid' => $modid, 'courseid' => $courseid));
 
-$blog_headers = blog_get_headers();
+// If action is add, we ignore $id to avoid any further problems
+if (!empty($id) && $action == 'add') {
+    $id = null;
+}
+
+$returnurl = new moodle_url($CFG->wwwroot . '/blog/index.php');
+
+if (!empty($courseid) && empty($modid)) {
+    $returnurl->param('courseid', $courseid);
+    $PAGE->set_context(get_context_instance(CONTEXT_COURSE, $courseid));
+}
+
+// If a modid is given, guess courseid
+if (!empty($modid)) {
+    $returnurl->param('modid', $modid);
+    $courseid = $DB->get_field('course_modules', 'course', array('id' => $modid));
+    $returnurl->param('courseid', $courseid);
+    $PAGE->set_context(get_context_instance(CONTEXT_MODULE, $modid));
+}
+
+$blogheaders = blog_get_headers();
 
 require_login($courseid);
 
@@ -58,117 +77,120 @@
     print_error('cannoteditentryorblog');
 }
 
-$returnurl = new moodle_url($CFG->wwwroot . '/blog/index.php');
-
-// Make sure that the person trying to edit have access right
+// Make sure that the person trying to edit has access right
 if ($id) {
-    if (!$existing = new blog_entry($id)) {
+    if (!$entry = new blog_entry($id)) {
         print_error('wrongentryid', 'blog');
     }
 
-    if (!blog_user_can_edit_entry($existing)) {
+    if (!blog_user_can_edit_entry($entry)) {
         print_error('notallowedtoedit', 'blog');
     }
-    $userid    = $existing->userid;
-    $returnurl->param('userid', $existing->userid);
+    $userid = $entry->userid;
 } else {
     if (!has_capability('moodle/blog:create', $sitecontext)) {
         print_error('noentry', 'blog'); // manageentries is not enough for adding
     }
-    $existing  = false;
+    $entry  = new stdClass();
+    $entry->id = null;
-    $userid    = $USER->id;
+    $userid = $USER->id;
-    $returnurl->param('userid', $userid);
-}
-
-if (!empty($courseid) && empty($modid)) {
-    $returnurl->param('courseid', $courseid);
-    $PAGE->set_context(get_context_instance(CONTEXT_COURSE, $courseid));
-}
-
-// If a modid is given, guess courseid
-if (!empty($modid)) {
-    $returnurl->param('modid', $modid);
-    $courseid = $DB->get_field('course_modules', 'course', array('id' => $modid));
-    $returnurl->param('courseid', $courseid);
-    $PAGE->set_context(get_context_instance(CONTEXT_MODULE, $modid));
 }
+$returnurl->param('userid', $userid);
 
 $strblogs = get_string('blogs','blog');
 
 if ($action === 'delete'){
-    if (!$existing) {
+    if (empty($entry->id)) {
         print_error('wrongentryid', 'blog');
     }
     if (data_submitted() && $confirm && confirm_sesskey()) {
-        $existing->delete();
+        // Make sure the current user is the author of the blog entry, or has some deleteanyentry capability
+        if (!blog_user_can_edit_entry($entry)) {
+            print_error('nopermissionstodeleteentry', 'blog');
+        } else {
+            $entry->delete();
-        redirect($returnurl);
+            redirect($returnurl);
-    } else {
+        }
+    } else if (blog_user_can_edit_entry($entry)) {
         $optionsyes = array('entryid'=>$id, 'action'=>'delete', 'confirm'=>1, 'sesskey'=>sesskey(), 'courseid'=>$courseid);
-        $optionsno = array('userid'=>$existing->userid, 'courseid'=>$courseid);
+        $optionsno = array('userid'=>$entry->userid, 'courseid'=>$courseid);
         $PAGE->set_title("$SITE->shortname: $strblogs");
         $PAGE->set_heading($SITE->fullname);
         echo $OUTPUT->header();
-        //blog_print_entry($existing);
-        $existing->print_html();
+        $entry->print_html();
         echo '<br />';
         echo $OUTPUT->confirm(get_string('blogdeleteconfirm', 'blog'), new moodle_url('edit.php', $optionsyes),new moodle_url( 'index.php', $optionsno));
         echo $OUTPUT->footer();
         die;
     }
+} else if ($action == 'add') {
+    $PAGE->set_title("$SITE->shortname: $strblogs: " . get_string('addnewentry', 'blog'));
+    $PAGE->set_heading($SITE->shortname);
+} else if ($action == 'edit') {
+    $PAGE->set_title("$SITE->shortname: $strblogs: " . get_string('editentry', 'blog'));
+    $PAGE->set_heading($SITE->shortname);
 }
 
-require_once('edit_form.php');
-
-if (!empty($existing)) {
-    if ($blogassociations = $DB->get_records('blog_association', array('blogid' => $existing->id))) {
+if (!empty($entry->id)) {
+    if ($CFG->useblogassociations && ($blogassociations = $DB->get_records('blog_association', array('blogid' => $entry->id)))) {
 
         foreach ($blogassociations as $assocrec) {
             $contextrec = $DB->get_record('context', array('id' => $assocrec->contextid));
 
             switch ($contextrec->contextlevel) {
                 case CONTEXT_COURSE:
-                    $existing->courseassoc = $assocrec->contextid;
+                    $entry->courseassoc = $assocrec->contextid;
                     break;
                 case CONTEXT_MODULE:
-                    $existing->modassoc[] = $assocrec->contextid;
+                    $entry->modassoc = $assocrec->contextid;
                     break;
             }
         }
     }
 }
 
-$textfieldoptions = array('trusttext'=>true, 'subdirs'=>true);
-$blogeditform = new blog_edit_form(null, compact('existing', 'sitecontext', 'textfieldoptions', 'id'));
-$draftitemid = file_get_submitted_draft_itemid('attachments');
-file_prepare_draft_area($draftitemid, $PAGE->context->id, 'blog_attachment', empty($id)?null:$id);
+require_once('edit_form.php');
+$summaryoptions = array('subdirs'=>false, 'maxfiles'=> 99, 'maxbytes'=>$CFG->maxbytes, 'trusttext'=>true, 'context'=>$sitecontext);
+$attachmentoptions = array('subdirs'=>false, 'maxfiles'=> 99, 'maxbytes'=>$CFG->maxbytes);
+
+$blogeditform = new blog_edit_form(null, compact('entry', 'summaryoptions', 'attachmentoptions', 'sitecontext', 'courseid', 'modid'));
+
+$entry = file_prepare_standard_editor($entry, 'summary', $summaryoptions, $sitecontext, 'blog_post', $entry->id);
+$entry = file_prepare_standard_filemanager($entry, 'attachment', $attachmentoptions, $sitecontext, 'blog_attachment', $entry->id);
 
-$editordraftid = file_get_submitted_draft_itemid('summary');
-$currenttext = file_prepare_draft_area($editordraftid, $PAGE->context->id, 'blog_post', empty($id) ? null : $id, array('subdirs'=>true), @$existing->summary);
+if (!empty($CFG->usetags) && !empty($entry->id)) {
+    include_once($CFG->dirroot.'/tag/lib.php');
+    $entry->tags = tag_get_tags_array('post', $entry->id);
+}
 
-$data = array('id'=>$id, 'summary'=>array('text'=>$currenttext, 'format'=>FORMAT_HTML, 'itemid' => $editordraftid));
-$blogeditform->set_data($data); // set defaults
+$entry->action = $action;
+// set defaults
+$blogeditform->set_data($entry);
 
-if ($blogeditform->is_cancelled()){
+if ($blogeditform->is_cancelled()) {
     redirect($returnurl);
-} else if ($fromform = $blogeditform->get_data()){
+
+} else if ($data = $blogeditform->get_data()){
 
-    //save stuff in db
     switch ($action) {
         case 'add':
-            $blogentry = new blog_entry($fromform, $blogeditform);
-            $blogentry->summary = file_save_draft_area_files($fromform->summary['itemid'], $PAGE->context->id, 'blog_post', $blogentry->id, array('subdirs'=>true), $fromform->summary['text']);
+            $blogentry = new blog_entry(null, $data, $blogeditform);
             $blogentry->add();
+            $blogentry->edit($data, $blogeditform, $summaryoptions, $attachmentoptions);
         break;
 
         case 'edit':
-            if (!$existing) {
+            if (empty($entry->id)) {
                 print_error('wrongentryid', 'blog');
             }
-            $existing->edit($fromform, $blogeditform);
+
+            $entry->edit($data, $blogeditform, $summaryoptions, $attachmentoptions);
         break;
+
         default :
             print_error('invalidaction');
     }
+
     redirect($returnurl);
 }
 
@@ -181,68 +203,40 @@
         $strformheading = get_string('addnewentry', 'blog');
         $entry->action       = $action;
 
-        if ($courseid) {  //pre-select the course for associations
+        if ($CFG->useblogassociations) {
+
+            //pre-select the course for associations
+            if ($courseid) {
-            $context = get_context_instance(CONTEXT_COURSE, $courseid);
-            $entry->courseassoc = $context->id;
-        }
+                $context = get_context_instance(CONTEXT_COURSE, $courseid);
+                $entry->courseassoc = $context->id;
+            }
 
-        if ($modid) { //pre-select the mod for associations
+            //pre-select the mod for associations
+            if ($modid) {
-            $context = get_context_instance(CONTEXT_MODULE, $modid);
+                $context = get_context_instance(CONTEXT_MODULE, $modid);
-            $entry->modassoc = array($context->id);
+                $entry->modassoc = $context->id;
+            }
         }
         break;
 
     case 'edit':
-        if (!$existing) {
+        if (empty($entry->id)) {
             print_error('wrongentryid', 'blog');
         }
-
-        $entry->id           = $existing->id;
-        $entry->subject      = $existing->subject;
-        $entry->fakesubject  = $existing->subject;
-        $entry->summary      = $existing->summary;
-        $entry->fakesummary  = $existing->summary;
-        $entry->publishstate = $existing->publishstate;
-        $entry->format       = $existing->format;
-        $entry->tags         = tag_get_tags_array('blog_entries', $entry->id);
-        $entry->action       = $action;
-
-        if (!empty($existing->courseassoc)) {
-            $entry->courseassoc = $existing->courseassoc;
-        }
-
-        if (!empty($existing->modassoc)) {
-            $entry->modassoc = $existing->modassoc;
-        }
-
+        $entry->tags = tag_get_tags_array('post', $entry->id);
         $strformheading = get_string('updateentrywithid', 'blog');
 
         break;
+
     default :
         print_error('unknowaction');
 }
 
 $entry->modid = $modid;
 $entry->courseid = $courseid;
-$entry->attachments = $draftitemid;
-$entry->summary = array('text' => @$existing->summary, 'format' => empty($existing->summaryformat) ? FORMAT_HTML : $existing->summaryformat, 'itemid' => $editordraftid);
-$entry->summaryformat = (empty($existing->summaryformat)) ? FORMAT_HTML : $existing->summaryformat;
-$PAGE->requires->data_for_js('blog_edit_existing', $entry);
-
-// done here in order to allow deleting of entries with wrong user id above
-if (!$user = $DB->get_record('user', array('id'=>$userid))) {
-    print_error('invaliduserid');
-}
-
-$PAGE->requires->js('blog/edit_form.js');
 
 echo $OUTPUT->header();
-
-$blogeditform->set_data($entry);
 $blogeditform->display();
-
-$PAGE->requires->js_function_call('select_initial_course');
-
 echo $OUTPUT->footer();
 
 die;
Index: blog/edit_form.php
=========================================================
--- blog/edit_form.php	(revision 1.25)
+++ blog/edit_form.php	Wed Oct 28 16:11:52 WST 2009
@@ -21,29 +21,32 @@
     public $modnames = array();
 
     function definition() {
-        global $CFG, $COURSE, $USER, $DB, $PAGE;
+        global $CFG, $DB;
 
-        $mform    =& $this->_form;
+        $mform =& $this->_form;
 
-        $entryid  = $this->_customdata['id'];
-        $existing = $this->_customdata['existing'];
+        $entry = $this->_customdata['entry'];
+        $courseid = $this->_customdata['courseid'];
+        $modid = $this->_customdata['modid'];
+        $summaryoptions = $this->_customdata['summaryoptions'];
+        $attachmentoptions = $this->_customdata['attachmentoptions'];
         $sitecontext = $this->_customdata['sitecontext'];
 
         $mform->addElement('header', 'general', get_string('general', 'form'));
 
         $mform->addElement('text', 'subject', get_string('entrytitle', 'blog'), 'size="60"');
-        $mform->addElement('editor', 'summary', get_string('entrybody', 'blog'), null, array('trusttext'=>true, 'subdirs'=>true, 'maxfiles' => -1));
+        $mform->addElement('editor', 'summary_editor', get_string('entrybody', 'blog'), null, $summaryoptions);
 
         $mform->setType('subject', PARAM_TEXT);
         $mform->addRule('subject', get_string('emptytitle', 'blog'), 'required', null, 'client');
 
-        $mform->setType('summary', PARAM_RAW);
-        $mform->addRule('summary', get_string('emptybody', 'blog'), 'required', null, 'client');
-        $mform->setHelpButton('summary', array('writing', 'richtext2'), false, 'editorhelpbutton');
+        $mform->setType('summary_editor', PARAM_RAW);
+        $mform->addRule('summary_editor', get_string('emptybody', 'blog'), 'required', null, 'client');
+        $mform->setHelpButton('summary_editor', array('writing', 'richtext2'), false, 'editorhelpbutton');
 
         $mform->addElement('format', 'summaryformat', get_string('format'));
 
-        $mform->addElement('filemanager', 'attachments', get_string('attachment', 'forum'));
+        $mform->addElement('filemanager', 'attachment_filemanager', get_string('attachment', 'forum'), null, $attachmentoptions);
 
         //disable publishstate options that are not allowed
         $publishstates = array();
@@ -56,7 +59,7 @@
 
         $mform->addElement('select', 'publishstate', get_string('publishto', 'blog'), $publishstates);
         $mform->setHelpButton('publishstate', array('publish_state', get_string('publishto', 'blog'), 'blog'));
-
+        $mform->setDefault('publishstate', 0);
 
         if (!empty($CFG->usetags)) {
             $mform->addElement('header', 'tagshdr', get_string('tags', 'tag'));
@@ -66,45 +69,38 @@
         $allmodnames = array();
 
         if (!empty($CFG->useblogassociations)) {
+            if ((!empty($entry->courseassoc) || (!empty($courseid) && empty($modid))) && has_capability('moodle/blog:associatecourse', $sitecontext)) {
+                if (!empty($courseid)) {
+                    $course = $DB->get_record('course', array('id' => $courseid));
-            $mform->addElement('header', 'assochdr', get_string('associations', 'blog'));
+                    $mform->addElement('header', 'assochdr', get_string('associations', 'blog'));
-            $mform->addElement('static', 'assocdescription', '', get_string('assocdescription', 'blog'));
-            if (has_capability('moodle/site:doanything', get_context_instance(CONTEXT_USER, $USER->id))) {
-                $courses = get_courses('all', 'visible DESC, fullname ASC');
+                    $context = get_context_instance(CONTEXT_COURSE, $courseid);
+                    $a->coursename = $course->fullname;
+                    $contextid = $context->id;
-            } else {
+                } else {
-                $courses = get_my_courses($USER->id, 'visible DESC, fullname ASC');
+                    $sql = 'SELECT fullname FROM {course} cr LEFT JOIN {context} ct ON ct.instanceid = cr.id WHERE ct.id = ?';
+                    $a->coursename = $DB->get_field_sql($sql, array($entry->courseassoc));
+                    $contextid = $entry->courseassoc;
-            }
+                }
 
-            $coursenames[0] = 'none';
-
-            if (!empty($courses)) {
-
-                foreach ($courses as $course) {
-                    $coursenames[$course->context->id] = $course->fullname;
-                    $modinfo = get_fast_modinfo($course, $USER->id);
-                    $coursecontextpath = $DB->get_field('context', 'path', array('id' => $course->context->id));
-
-                    foreach ($modinfo->instances as $modname => $instances) {
-
-                        foreach ($instances as $modid => $mod) {
-                            $modcontextid = $DB->get_field_select('context', 'id',
-                                'instanceid = '.$mod->id.' AND ' .
-                                'contextlevel = ' . CONTEXT_MODULE . ' AND ' .
-                                'path LIKE \''.$coursecontextpath.'/%\'');
-
-                            $modstring = $mod->name . ' (' . get_plugin_name($modname) . ')';
-                            $this->modnames[$course->context->id][$modcontextid] = $modstring;
-                            $allmodnames[$modcontextid] = $course->shortname . " - " . $modstring;
-                        }
-                    }
-                }
+                $mform->addElement('advcheckbox', 'courseassoc', get_string('associatewithcourse', 'blog', $a), null, null, array(0, $contextid));
+                $mform->setDefault('courseassoc', $contextid);
+            } else if ((!empty($entry->modassoc) || !empty($modid)) && has_capability('moodle/blog:associatemodule', $sitecontext)) {
+                if (!empty($modid)) {
+                    $mod = get_coursemodule_from_id(false, $modid);
+                    $a->modtype = get_string('modulename', $mod->modname);
+                    $a->modname = $mod->name;
+                    $context = get_context_instance(CONTEXT_MODULE, $modid);
+                } else {
+                    $context = $DB->get_record('context', array('id' => $entry->modassoc));
+                    $cm = $DB->get_record('course_modules', array('id' => $context->instanceid));
+                    $a->modtype = $DB->get_field('modules', 'name', array('id' => $cm->module));
+                    $a->modname = $DB->get_field($a->modtype, 'name', array('id' => $cm->instance));
-            }
+                }
-            $mform->addElement('select', 'courseassoc', get_string('course'), $coursenames, 'onchange="addCourseAssociations()"');
-            $mform->setAdvanced('courseassoc');
-            $selectassoc = &$mform->addElement('select', 'modassoc', get_string('managemodules'), $allmodnames);
-            $mform->setAdvanced('modassoc');
-            $selectassoc->setMultiple(true);
-            $PAGE->requires->data_for_js('blog_edit_form_modnames', $this->modnames);
 
+                $mform->addElement('header', 'assochdr', get_string('associations', 'blog'));
+                $mform->addElement('advcheckbox', 'modassoc', get_string('associatewithmodule', 'blog', $a), null, null, array(0, $context->id));
+                $mform->setDefault('modassoc', $context->id);
+            }
         }
 
         $this->add_action_buttons();
@@ -112,40 +108,30 @@
         $mform->setType('action', PARAM_ACTION);
         $mform->setDefault('action', '');
 
-        $mform->addElement('hidden', 'courseid');
-        $mform->setType('courseid', PARAM_INT);
-
         $mform->addElement('hidden', 'entryid');
         $mform->setType('entryid', PARAM_INT);
-        $mform->setDefault('entryid', $entryid);
+        $mform->setDefault('entryid', $entry->id);
 
         $mform->addElement('hidden', 'modid');
         $mform->setType('modid', PARAM_INT);
-        $mform->setDefault('modid', 0);
+        $mform->setDefault('modid', $modid);
 
         $mform->addElement('hidden', 'courseid');
         $mform->setType('courseid', PARAM_INT);
-        $mform->setDefault('courseid', 0);
-
-        // $this->set_data($existing);
+        $mform->setDefault('courseid', $courseid);
     }
 
     function validation($data, $files) {
         global $CFG, $DB, $USER;
 
         $errors = array();
-
-        if (empty($data['courseassoc']) && ($data['publishstate'] == 'course' || $data['publishstate'] == 'group') && !empty($CFG->useblogassociations)) {
-            return array('publishstate' => get_string('mustassociatecourse', 'blog'));
-        }
+        $sitecontext = get_context_instance(CONTEXT_SYSTEM);
 
-        //validate course association
-        if (!empty($data['courseassoc'])) {
+        // validate course association
+        if (!empty($data['courseassoc']) && has_capability('moodle/blog:associatecourse', $sitecontext)) {
             $coursecontext = $DB->get_record('context', array('id' => $data['courseassoc'], 'contextlevel' => CONTEXT_COURSE));
 
-            if ($coursecontext)  {    //insure associated course has a valid context id
-                //insure the user has access to this course
-
+            if ($coursecontext)  {
                 if (!has_capability('moodle/course:view', $coursecontext, $USER->id)) {
                     $errors['courseassoc'] = get_string('studentnotallowed', '', fullname($USER, true));
                 }
@@ -154,34 +140,32 @@
             }
         }
 
-        //validate mod associations
+        // validate mod association
         if (!empty($data['modassoc'])) {
-            //insure mods are valid
+            $modcontextid = $data['modassoc'];
 
-            foreach ($data['modassoc'] as $modid) {
-                $modcontext = $DB->get_record('context', array('id' => $modid, 'contextlevel' => CONTEXT_MODULE));
+            $modcontext = $DB->get_record('context', array('id' => $modcontextid, 'contextlevel' => CONTEXT_MODULE));
 
-                if ($modcontext) {  //insure associated mod has a valid context id
-                    //get context of the mod's course
+            if ($modcontext) {
+                // get context of the mod's course
-                    $path = split('/', $modcontext->path);
-                    $coursecontext = $DB->get_record('context', array('id' => $path[(count($path) - 2)]));
+                $path = split('/', $modcontext->path);
+                $coursecontext = $DB->get_record('context', array('id' => $path[(count($path) - 2)]));
 
-                    //insure only one course is associated
+                // ensure only one course is associated
-                    if (!empty($data['courseassoc'])) {
-                        if ($data['courseassoc'] != $coursecontext->id) {
-                            $errors['modassoc'] = get_string('onlyassociateonecourse', 'blog');
-                        }
-                    } else {
-                        $data['courseassoc'] = $coursecontext->id;
-                    }
+                if (!empty($data['courseassoc'])) {
+                    if ($data['courseassoc'] != $coursecontext->id) {
+                        $errors['modassoc'] = get_string('onlyassociateonecourse', 'blog');
+                    }
+                } else {
+                    $data['courseassoc'] = $coursecontext->id;
+                }
 
-                    //insure the user has access to each mod's course
+                // ensure the user has access to each mod's course
-                    if (!has_capability('moodle/course:view', $coursecontext)) {
-                        $errors['modassoc'] = get_string('studentnotallowed', '', fullname($USER, true));
-                    }
-                } else {
-                    $errors['modassoc'] = get_string('invalidcontextid', 'blog');
+                if (!has_capability('moodle/course:view', $coursecontext)) {
+                    $errors['modassoc'] = get_string('studentnotallowed', '', fullname($USER, true));
+                }
+            } else {
+                $errors['modassoc'] = get_string('invalidcontextid', 'blog');
-                }
             }
         }
 
@@ -190,24 +174,4 @@
         }
         return true;
     }
-
-    /**
-     * This function sets up options of otag select element. This is called from definition and also
-     * after adding new official tags with the add tag button.
-     *
-     */
-    function otags_select_setup(){
-        global $DB;
-
-        $mform =& $this->_form;
-        if ($otagsselect =& $mform->getElement('otags')) {
-            $otagsselect->removeOptions();
-        }
-        $namefield = empty($CFG->keeptagnamecase) ? 'name' : 'rawname';
-        if ($otags = $DB->get_records_sql_menu("SELECT id, $namefield FROM {tag} WHERE tagtype='official' ORDER by $namefield ASC")) {
-            $otagsselect->loadArray($otags);
-        }
-
-    }
-
 }
Index: blog/external_blog_edit.php
=========================================================
--- blog/external_blog_edit.php	(revision 0)
+++ blog/external_blog_edit.php	Fri Oct 23 07:55:06 WST 2009
@@ -0,0 +1,127 @@
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+
+/**
+ * Form page for an external blog link.
+ *
+ * @package    moodlecore
+ * @subpackage blog
+ * @copyright  2009 Nicolas Connault
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once('../config.php');
+require_once('lib.php');
+require_once('external_blog_edit_form.php');
+require_once($CFG->libdir . '/simplepie/moodle_simplepie.php');
+require_once($CFG->dirroot.'/tag/lib.php');
+
+require_login();
+require_capability('moodle/blog:manageexternal', get_context_instance(CONTEXT_SYSTEM));
+
+// TODO redirect if $CFG->useexternalblogs is off, $CFG->maxexternalblogsperuser == 0, or if user doesn't have caps to manage external blogs
+
+$id = optional_param('id', null, PARAM_INT);
+$PAGE->set_url('/blog/external_blog_edit.php', array('id' => $id));
+
+$returnurl = new moodle_url($CFG->wwwroot . '/blog/external_blogs.php');
+
+$action = (empty($id)) ? 'add' : 'edit';
+
+$external = new stdClass();
+
+// Check that this id exists
+if (!empty($id) && !$DB->record_exists('blog_external', array('id' => $id))) {
+    print_error('wrongexternalid', 'blog');
+} elseif (!empty($id)) {
+    $external = $DB->get_record('blog_external', array('id' => $id));
+}
+
+$strformheading = ($action == 'edit') ? get_string('editexternalblog', 'blog') : get_string('addnewexternalblog', 'blog');
+$strexternalblogs = get_string('externalblogs','blog');
+$strblogs = get_string('blogs','blog');
+
+$externalblogform = new blog_edit_external_form();
+
+if ($externalblogform->is_cancelled()){
+    redirect($returnurl);
+
+} else if ($data = $externalblogform->get_data()) {
+    //save stuff in db
+    switch ($action) {
+        case 'add':
+            $rss = new moodle_simplepie($data->url);
+
+            $newexternal = new stdClass();
+            $newexternal->name = (empty($data->name)) ? $rss->get_title() : $data->name;
+            $newexternal->description = (empty($data->description)) ? $rss->get_description() : $data->description;
+            $newexternal->userid = $USER->id;
+            $newexternal->url = $data->url;
+            $newexternal->filtertags = $data->filtertags;
+            $newexternal->timemodified = mktime();
+
+            if ($newexternal->id = $DB->insert_record('blog_external', $newexternal)) {
+                blog_sync_external_entries($newexternal);
+                tag_set('blog_external', $newexternal->id, $data->autotags);
+            }
+
+            break;
+
+        case 'edit':
+            if ($data->id && $DB->record_exists('blog_external', array('id' => $data->id))) {
+
+                $rss = new moodle_simplepie($data->url);
+
+                $external->id = $data->id;
+                $external->name = (empty($data->name)) ? $rss->get_title() : $data->name;
+                $external->description = (empty($data->description)) ? $rss->get_description() : $data->description;
+                $external->userid = $USER->id;
+                $external->url = $data->url;
+                $external->filtertags = $data->filtertags;
+                $external->timemodified = mktime();
+
+                if ($DB->update_record('blog_external', $external)) {
+                    tag_set('blog_external', $external->id, explode(',', $data->autotags));
+                }
+
+            } else {
+                print_error('wrongexternalid', 'blog');
+            }
+
+            break;
+
+        default :
+            print_error('invalidaction');
+    }
+
+    redirect($returnurl);
+}
+
+$PAGE->navbar->add(fullname($USER), new moodle_url($CFG->wwwroot.'/user/view.php', array('id'=>$USER->id)));
+$PAGE->navbar->add($strblogs, new moodle_url($CFG->wwwroot.'/blog/index.php', array('userid'=>$USER->id)));
+$PAGE->navbar->add($strformheading);
+$PAGE->set_heading("$SITE->shortname: $strblogs: $strexternalblogs", $SITE->fullname);
+$PAGE->set_title("$SITE->shortname: $strblogs: $strexternalblogs");
+
+echo $OUTPUT->header();
+echo $OUTPUT->heading($strformheading, 2);
+
+$externalblogform->set_data($external);
+$externalblogform->display();
+
+echo $OUTPUT->footer();
Index: blog/external_blog_edit_form.php
=========================================================
--- blog/external_blog_edit_form.php	(revision 0)
+++ blog/external_blog_edit_form.php	Mon Oct 26 17:26:26 WST 2009
@@ -0,0 +1,116 @@
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+
+/**
+ * Moodleform for the user interface for managing external blog links.
+ *
+ * @package    moodlecore
+ * @subpackage blog
+ * @copyright  2009 Nicolas Connault
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once($CFG->libdir.'/formslib.php');
+
+class blog_edit_external_form extends moodleform {
+    public function definition() {
+        global $CFG;
+
+        $mform =& $this->_form;
+
+        $mform->addElement('text', 'url', get_string('url'), array('size' => 50));
+        $mform->addRule('url', get_string('emptyurl', 'blog'), 'required', null, 'client');
+        $mform->setHelpButton('url', array('url', get_string('url', 'blog'), 'blog'));
+
+        $mform->addElement('text', 'name', get_string('name'));
+        $mform->setHelpButton('name', array('name', get_string('name', 'blog'), 'blog'));
+
+        $mform->addElement('textarea', 'description', get_string('description'), array('cols' => 50, 'rows' => 7));
+        $mform->setHelpButton('description', array('description', get_string('description', 'blog'), 'blog'));
+
+        if (!empty($CFG->usetags)) {
+            $mform->addElement('text', 'filtertags', get_string('filtertags', 'blog'), array('size' => 50));
+            $mform->setHelpButton('filtertags', array('filtertags', get_string('filtertags', 'blog'), 'blog'));
+            $mform->addElement('text', 'autotags', get_string('autotags', 'blog'), array('size' => 50));
+        }
+
+        $this->add_action_buttons();
+
+        $mform->addElement('hidden', 'id');
+        $mform->setType('id', PARAM_INT);
+        $mform->setDefault('id', 0);
+
+        $mform->addElement('hidden', 'returnurl');
+        $mform->setType('returnurl', PARAM_URL);
+        $mform->setDefault('returnurl', 0);
+    }
+
+    /**
+     * Additional validation includes checking URL and tags
+     */
+    public function validation($data, $files) {
+        global $CFG;
+
+        $errors = parent::validation($data, $files);
+
+        require_once($CFG->libdir . '/simplepie/moodle_simplepie.php');
+
+        $rssfile = new moodle_simplepie_file($data['url']);
+        $filetest = new SimplePie_Locator($rssfile);
+
+        if (!$filetest->is_feed($rssfile)) {
+            $errors['url'] = get_string('feedisinvalid', 'blog');
+        } else {
+            $rss = new moodle_simplepie($data['url']);
+            if (!$rss->init()) {
+                $errors['url'] = get_string('emptyrssfeed', 'blog');
+            }
+        }
+
+        return $errors;
+    }
+
+    public function definition_after_data() {
+        global $CFG, $COURSE;
+        $mform =& $this->_form;
+
+        $name = trim($mform->getElementValue('name'));
+        $description = trim($mform->getElementValue('description'));
+        $url = $mform->getElementValue('url');
+
+        if (empty($name) || empty($description)) {
+            $rss = new moodle_simplepie($url);
+
+            if (empty($name) && $rss->get_title()) {
+                $mform->setDefault('name', $rss->get_title());
+            }
+
+            if (empty($description) && $rss->get_description()) {
+                $mform->setDefault('description', $rss->get_description());
+            }
+        }
+
+        if ($id = $mform->getElementValue('id')) {
+            $mform->setDefault('autotags', implode(',', tag_get_tags_array('blog_external', $id)));
+            $mform->freeze('url');
+            $mform->freeze('filtertags');
+            // TODO change the filtertags element to a multiple select, using the tags of the external blog
+            // Use $rss->get_channel_tags()
+        }
+    }
+}
Index: blog/external_blogs.php
=========================================================
--- blog/external_blogs.php	(revision 0)
+++ blog/external_blogs.php	Fri Oct 23 08:51:34 WST 2009
@@ -0,0 +1,103 @@
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+
+/**
+ * List of external blogs for current user.
+ *
+ * @package    moodlecore
+ * @subpackage blog
+ * @copyright  2009 Nicolas Connault
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once('../config.php');
+require_once('lib.php');
+
+require_login();
+
+$PAGE->set_url('/blog/external_blogs.php');
+require_capability('moodle/blog:manageexternal', get_context_instance(CONTEXT_SYSTEM));
+
+$delete = optional_param('delete', null, PARAM_INT);
+
+$strexternalblogs = get_string('externalblogs','blog');
+$straddnewexternalblog = get_string('addnewexternalblog','blog');
+$strblogs = get_string('blogs','blog');
+$message = null;
+
+if ($delete) {
+    $DB->delete_records('blog_external', array('id' => $delete));
+    $message = get_string('externalblogdeleted', 'blog');
+}
+
+$blogs = $DB->get_records('blog_external', array('userid' => $USER->id));
+
+$PAGE->navbar->add(fullname($USER), new moodle_url($CFG->wwwroot.'/user/view.php', array('id'=>$USER->id)));
+$PAGE->navbar->add($strblogs, new moodle_url($CFG->wwwroot.'/blog/index.php', array('userid'=>$USER->id)));
+$PAGE->navbar->add($strexternalblogs);
+$PAGE->set_heading("$SITE->shortname: $strblogs: $strexternalblogs", $SITE->fullname);
+$PAGE->set_title("$SITE->shortname: $strblogs: $strexternalblogs");
+
+echo $OUTPUT->header();
+echo $OUTPUT->heading($strexternalblogs, 2);
+
+if (!empty($message)) {
+    echo $OUTPUT->notification($message);
+}
+
+echo $OUTPUT->box_start('generalbox boxaligncenter');
+
+if (!empty($blogs)) {
+    $table = new html_table();
+    $table->cellpadding = 4;
+    $table->add_class('generaltable boxaligncenter');
+    $table->head = array(get_string('name'), get_string('url'), get_string('timefetched', 'blog'), get_string('valid', 'blog'), get_string('actions'));
+
+    foreach ($blogs as $blog) {
+        $validicon = html_image::make($OUTPUT->old_icon_url('i/tick_green_big'));
+        $validicon->alt = get_string('feedisvalid', 'blog');
+        $validicon->title = get_string('feedisvalid', 'blog');
+
+        if ($blog->failedlastsync) {
+            $validicon->src = $OUTPUT->old_icon_url('i/cross_red_big');
+            $validicon->alt = get_string('feedisinvalid', 'blog');
+            $validicon->title = get_string('feedisinvalid', 'blog');
+        }
+
+        $editicon = new moodle_action_icon;
+        $editicon->link->url = new moodle_url($CFG->wwwroot.'/blog/external_blog_edit.php', array('id' => $blog->id));
+        $editicon->link->title = get_string('editexternalblog', 'blog');
+        $editicon->image->src = $OUTPUT->old_icon_url('t/edit');
+        $editicon->image->alt = get_string('editexternalblog', 'blog');
+
+        $deleteicon = new moodle_action_icon;
+        $deleteicon->link->url = new moodle_url($CFG->wwwroot.'/blog/external_blogs.php', array('delete' => $blog->id));
+        $deleteicon->link->title = get_string('deleteexternalblog', 'blog');
+        $deleteicon->image->src = $OUTPUT->old_icon_url('t/delete');
+        $deleteicon->image->alt = get_string('deleteexternalblog', 'blog');
+
+        $icons = $OUTPUT->action_icon($editicon) . $OUTPUT->action_icon($deleteicon);
+        $table->data[] = html_table_row::make(array($blog->name, $blog->url, userdate($blog->timefetched), $OUTPUT->image($validicon), $icons));
+    }
+    echo $OUTPUT->table($table);
+}
+
+$newexternalurl = new moodle_url($CFG->wwwroot.'/blog/external_blog_edit.php');
+echo $OUTPUT->link(html_link::make($newexternalurl, $straddnewexternalblog));
+echo $OUTPUT->box_end();
+echo $OUTPUT->footer();
Index: blog/index.php
=========================================================
--- blog/index.php	(revision 1.55)
+++ blog/index.php	Fri Oct 23 10:30:00 WST 2009
@@ -13,16 +13,16 @@
 require_once($CFG->dirroot .'/tag/lib.php');
 require_once($CFG->libdir .'/commentlib.php');
 
-$id           = optional_param('id', null, PARAM_INT);
-$start        = optional_param('formstart', 0, PARAM_INT);
-$tag          = optional_param('tag', '', PARAM_NOTAGS);
-$userid       = optional_param('userid', null, PARAM_INT);
-$tagid        = optional_param('tagid', null, PARAM_INT);
-$modid        = optional_param('modid', null, PARAM_INT);
-$entryid      = optional_param('entryid', null, PARAM_INT);
-$groupid      = optional_param('groupid', null, PARAM_INT);
+$id       = optional_param('id', null, PARAM_INT);
+$start    = optional_param('formstart', 0, PARAM_INT);
+$tag      = optional_param('tag', '', PARAM_NOTAGS);
+$userid   = optional_param('userid', null, PARAM_INT);
+$tagid    = optional_param('tagid', null, PARAM_INT);
+$modid    = optional_param('modid', null, PARAM_INT);
+$entryid  = optional_param('entryid', null, PARAM_INT);
+$groupid  = optional_param('groupid', null, PARAM_INT);
-$courseid     = optional_param('courseid', null, PARAM_INT); // needed for user tabs and course tracking
+$courseid = optional_param('courseid', null, PARAM_INT);
-$search       = optional_param('search', null, PARAM_RAW);
+$search   = optional_param('search', null, PARAM_RAW);
 
 comment::js();
 
@@ -35,8 +35,9 @@
 $PAGE->set_url('blog/index.php', $url_params);
 
 //correct tagid if a text tag is provided as a param
-if (!empty($tag)) {  //text tag parameter takes precedence
-    if ($tagrec = $DB->get_record_sql("SELECT * FROM {tag} WHERE name LIKE ?", array($tag))) {
+if (!empty($tag)) {
+    $ILIKE = $DB->sql_ilike();
+    if ($tagrec = $DB->get_record_sql("SELECT * FROM {tag} WHERE name $ILIKE ?", array("%$tag%"))) {
         $tagid = $tagrec->id;
     } else {
         unset($tagid);
@@ -61,19 +62,16 @@
 
 if (!$userid && has_capability('moodle/blog:view', $sitecontext) && $CFG->bloglevel > BLOG_USER_LEVEL) {
     if ($entryid) {
-        if (!$entryobject = $DB->get_record('blog_entries', array('id'=>$entryid))) {
+        if (!$entryobject = $DB->get_record('post', array('id'=>$entryid))) {
             print_error('nosuchentry', 'blog');
         }
         $userid = $entryobject->userid;
     }
 } else if (!$userid) {
-    // user might have capability to write blogs, but not read blogs at site level
-    // users might enter this url manually without parameters
     $userid = $USER->id;
 }
-/// check access and prepare filters
 
-if (!empty($modid)) {  //check mod access
+if (!empty($modid)) {
     if ($CFG->bloglevel < BLOG_SITE_LEVEL) {
         print_error(get_string('nocourseblogs', 'blog'));
     }
@@ -83,7 +81,7 @@
     $courseid = $mod->course;
 }
 
-if ((empty($courseid) ? true : $courseid == SITEID) && empty($userid)) {  //check site access
+if ((empty($courseid) ? true : $courseid == SITEID) && empty($userid)) {
     if ($CFG->bloglevel < BLOG_SITE_LEVEL) {
         print_error('siteblogdisable', 'blog');
     }
@@ -120,8 +118,7 @@
         print_error('groupblogdisable', 'blog');
     }
 
-        // fix for MDL-9268
-    if (! $group = groups_get_group($groupid)) { //TODO:check.
+    if (! $group = groups_get_group($groupid)) {
         print_error(get_string('invalidgroupid', 'blog'));
     }
 
@@ -182,47 +179,18 @@
 $courseid = (empty($courseid)) ? SITEID : $courseid;
 
 if (!empty($courseid)) {
-    $filters['course'] = $courseid;
     $PAGE->set_context(get_context_instance(CONTEXT_COURSE, $courseid));
 }
 
 if (!empty($modid)) {
-    $filters['module'] = $modid;
     $PAGE->set_context(get_context_instance(CONTEXT_MODULE, $modid));
 }
 
-if (!empty($groupid)) {
-    $filters['group'] = $groupid;
-}
-
-if (!empty($userid)) {
-    $filters['user'] = $userid;
-}
-
-if (!empty($tagid)) {
-    $filters['tag'] = $tagid;
-}
-
-if (!empty($search)) {
-    $filters['search'] = $search;
-}
-
-if (!empty($entryid)) {
-    $filters['entry'] = $entryid;
-}
 $blogheaders = blog_get_headers();
 blog_extend_settings_navigation($PAGE->settingsnav);
 
-// prints the tabs
-$showroles = !empty($userid);
-$currenttab = 'blogs';
-
-$user = $USER;
-$userid = $USER->id;
-
-// Unless this is a user's blog listing, the context is system
 if (empty($entryid) && empty($modid) && empty($groupid)) {
-    $PAGE->set_context(get_context_instance(CONTEXT_USER, $userid));
+    $PAGE->set_context(get_context_instance(CONTEXT_USER, $USER->id));
 }
 
 echo $OUTPUT->header();
@@ -233,8 +201,8 @@
 
 echo $OUTPUT->heading($blogheaders['heading'], 2);
 
-$blog_listing = new blog_listing($filters);
-$blog_listing->print_entries();
+$bloglisting = new blog_listing($blogheaders['filters']);
+$bloglisting->print_entries();
 
 echo $OUTPUT->footer();
 
Index: blog/lib.php
=========================================================
--- blog/lib.php	(revision 1.127)
+++ blog/lib.php	Thu Oct 29 09:54:31 WST 2009
@@ -32,61 +32,14 @@
 require_once($CFG->dirroot.'/tag/lib.php');
 
 /**
- * Definition of blogcourse page type (blog page with course id present).
- */
-//not used at the moment, and may not need to be
-define('PAGE_BLOG_COURSE_VIEW', 'blog_course-view');
-define('BLOG_PUBLISHSTATE_DRAFT', 0);
-define('BLOG_PUBLISHSTATE_SITE', 1);
-define('BLOG_PUBLISHSTATE_PUBLIC', 2);
-
-/**
- * Checks to see if user has visited blogpages before, if not, install 2
- * default blocks (blog_menu and blog_tags).
- */
-function blog_check_and_install_blocks() {
-    global $USER, $DB;
-
-    if (isloggedin() && !isguestuser()) {
-        // if this user has not visited this page before
-        if (!get_user_preferences('blogpagesize')) {
-            // find the correct ids for blog_menu and blog_from blocks
-            $menublock = $DB->get_record('block', array('name'=>'blog_menu'));
-            $tagsblock = $DB->get_record('block', array('name'=>'blog_tags'));
-            // add those 2 into block_instance page
-
-// Commmented out since the block changes broke it. Hopefully nico will fix it ;-)
-//                // add blog_menu block
-//                $newblock = new object();
-//                $newblock->blockid  = $menublock->id;
-//                $newblock->pageid   = $USER->id;
-//                $newblock->pagetype = 'blog-view';
-//                $newblock->position = 'r';
-//                $newblock->weight   = 0;
-//                $newblock->visible  = 1;
-//                $DB->insert_record('block_instances', $newblock);
-//
-//                // add blog_tags menu
-//                $newblock -> blockid = $tagsblock->id;
-//                $newblock -> weight  = 1;
-//                $DB->insert_record('block_instances', $newblock);
-
-            // finally we set the page size pref
-            set_user_preference('blogpagesize', 10);
-        }
-    }
-}
-
-
-/**
  * User can edit a blog entry if this is their own blog entry and they have
  * the capability moodle/blog:create, or if they have the capability
  * moodle/blog:manageentries.
  *
  * This also applies to deleting of entries.
  */
-function blog_user_can_edit_entry($blog_entry) {
-    global $CFG, $USER, $OUTPUT;
+function blog_user_can_edit_entry($blogentry) {
+    global $USER;
 
     $sitecontext = get_context_instance(CONTEXT_SYSTEM);
 
@@ -94,8 +47,7 @@
         return true; // can edit any blog entry
     }
 
-    if ($blog_entry->userid == $USER->id
-      and has_capability('moodle/blog:create', $sitecontext)) {
+    if ($blogentry->userid == $USER->id && has_capability('moodle/blog:create', $sitecontext)) {
         return true; // can edit own when having blog:create capability
     }
 
@@ -108,14 +60,14 @@
  * Only blog level is checked here, the capabilities are enforced
  * in blog/index.php
  */
-function blog_user_can_view_user_entry($targetuserid, $blog_entry=null) {
+function blog_user_can_view_user_entry($targetuserid, $blogentry=null) {
     global $CFG, $USER, $DB;
 
     if (empty($CFG->bloglevel)) {
         return false; // blog system disabled
     }
 
-    if (!empty($USER->id) and $USER->id == $targetuserid) {
+    if (!empty($USER->id) && $USER->id == $targetuserid) {
         return true; // can view own entries in any case
     }
 
@@ -125,12 +77,12 @@
     }
 
     // coming for 1 entry, make sure it's not a draft
-    if ($blog_entry and $blog_entry->publishstate == 'draft') {
+    if ($blogentry && $blogentry->publishstate == 'draft' && !has_capability('moodle/blog:viewdrafts', $sitecontext)) {
         return false;  // can not view draft of others
     }
 
     // coming for 1 entry, make sure user is logged in, if not a public blog
-    if ($blog_entry && $blog_entry->publishstate != 'public' && !isloggedin()) {
+    if ($blogentry && $blogentry->publishstate != 'public' && !isloggedin()) {
         return false;
     }
 
@@ -160,157 +112,109 @@
  * @param int userid - id of user whose blog associations will be deleted
  */
 function blog_remove_associations_for_user($userid) {
-     global $DB;
+    global $DB;
-     foreach(blog_fetch_entries(array('user' => $userid), 'lasmodified DESC') as $entry) {
+    $blogentries = blog_fetch_entries(array('user' => $userid), 'lasmodified DESC');
+    foreach ($blogentries as $entry) {
+        if (blog_user_can_edit_entry($entry)) {
-         blog_remove_associations_for_entry($entry->id);
-     }
- }
+            blog_remove_associations_for_entry($entry->id);
+        }
+    }
+}
 
 /**
- * generates the url of the page displaying entries matching the search criteria
- *  @param array filters an array of filters (filtername => filtervalue) to narrow down results by
- *  available filters:
- *    entry: id field of a specific entry
- *    course: id of a course that the entries must be associated with
- *    mod: id of a course module that the entries must be associated with
- *    user: id of a user who must be the author of an entry
- *    group: id of a group who the author must be a member of, and whose course must be associated with the entry
- *    tag: id of a tag that must be applied to the entry
- *    site: the entire site is searched
- *  @return string the url of the page displaying entries matching the search criteria
+ * remove all associations for the blog entries of a particular course
+ * @param int courseid - id of user whose blog associations will be deleted
  */
-function blog_get_blogs_url($filters) {
-    global $CFG;
-    $blogsurl = new moodle_url($CFG->wwwroot . '/blog/index.php');
-    if (!empty($filters['course'])) {
-        $blogsurl->param('courseid', $filters['course']);
-    }
-    if (!empty($filters['mod'])) {
-        $blogsurl->param('modid', $filters['mod']);
-    }
-    if (!empty($filters['group'])) {
-        $blogsurl->param('groupid', $filters['group']);
-    }
-    if (!empty($filters['user'])) {
-        $blogsurl->param('userid', $filters['user']);
-    }
-    if (!empty($filters['entry'])) {
-        $blogsurl->param('entryid', $filters['entry']);
-    }
-    if (!empty($filters['tag'])) {
-        $blogsurl->param('tagid', $filters['tag']);
-    }
-    if (!empty($filters['tagtext'])) {
-        $blogsurl->param('tag', $filters['tagtext']);
-    }
-    return $blogsurl;
+function blog_remove_associations_for_course($courseid) {
+    global $DB;
+    $context = get_context_instance(CONTEXT_COURSE, $courseid);
+    $DB->delete_records('blog_association', array('contextid' => $context->id));
 }
 
 /**
- * A simple function for checking if a given URL is valid and resolves
- * to a proper XML data stream.
+ * Given a record in the {blog_external} table, checks the blog's URL
+ * for new entries not yet copied into Moodle.
  *
- * @param string $url
- * @return bool
+ * @param object $externalblog
+ * @return boolean False if the Feed is invalid
  */
-function blog_is_valid_url($url) {
-    $url = @parse_url($url);
+function blog_sync_external_entries($externalblog) {
+    global $CFG, $DB;
+    require_once($CFG->libdir . '/simplepie/moodle_simplepie.php');
 
-    if (!$url) {
+    $rssfile = new moodle_simplepie_file($externalblog->url);
+    $filetest = new SimplePie_Locator($rssfile);
+
+    if (!$filetest->is_feed($rssfile)) {
+        $externalblog->failedlastsync = 1;
+        $DB->update_record('blog_external', $externalblog);
         return false;
+    } else if ($externalblog->failedlastsync) {
+        $externalblog->failedlastsync = 0;
+        $DB->update_record('blog_external', $externalblog);
     }
 
-    $url = array_map('trim', $url);
+    // Delete all blog entries associated with this external blog
+    blog_delete_external_entries($externalblog);
 
-    if (empty($url['port'])) {
-        $url['port'] = 80;
-    } else {
-        $url['port'] = (int)$url['port'];
-    }
+    $rss = new moodle_simplepie($externalblog->url);
 
-    $path = '';
-    if (!empty($url['path'])) {
-        $path = $url['path'];
+    if (empty($rss->data)) {
+        return null;
     }
 
+    foreach ($rss->get_items() as $entry) {
+        // If filtertags are defined, use them to filter the entries by RSS category
+        if (!empty($externalblog->filtertags)) {
+            $containsfiltertag = false;
+            $categories = $entry->get_categories();
+            $filtertags = explode(',', $externalblog->filtertags);
+            $filtertags = array_map('trim', $filtertags);
+            $filtertags = array_map('strtolower', $filtertags);
 
-    if ($path == '') {
-        $path = '/';
+            foreach ($categories as $category) {
+                if (in_array(trim(strtolower($category->term)), $filtertags)) {
+                    $containsfiltertag = true;
+                }
-    }
+            }
 
-    if (!empty($url['query'])) {
-        $path .= "?{$url['query']}";
+            if (!$containsfiltertag) {
+                continue;
+            }
-    }
+        }
 
-    if (isset($url['host']) && $url['host'] != gethostbyname($url['host'])) {
-        if (PHP_VERSION >= 5) {
-            $headers = get_headers("{$url['scheme']}://{$url['host']}:{$url['port']}$path");
-        } else {
-            $fp = fsockopen($url['host'], $url['port'], $errno, $errstr, 30);
+        $newentry = new object();
+        $newentry->userid = $externalblog->userid;
+        $newentry->module = 'blog_external';
+        $newentry->content = $externalblog->id;
+        $newentry->uniquehash = $entry->get_permalink();
+        $newentry->publishstate = 'site';
+        $newentry->format = FORMAT_HTML;
+        $newentry->subject = $entry->get_title();
+        $newentry->summary = $entry->get_description();
+        $newentry->created = $entry->get_date('U');
+        $newentry->lastmodified = $entry->get_date('U');
 
-            if (!$fp) {
-                return false;
-            }
+        $id = $DB->insert_record('post', $newentry);
 
-            fputs($fp, "HEAD $path HTTP/1.1\r\nHost: {$url['host']}\r\n\r\n");
-            $headers = fread($fp, 128);
-            fclose($fp);
+        // Set tags
+        if ($tags = tag_get_tags_array('blog_external', $externalblog->id)) {
+            tag_set('post', $id, $tags);
         }
-
-        if (is_array($headers)) {
-            $headers = implode("\n", $headers);
-        }
+    }
 
-        return (bool) preg_match('#^HTTP/.*\s+[(200|301|302)]+\s#i', $headers);
-    }
-    return false;
+    $DB->update_record('blog_external', array('id' => $externalblog->id, 'timefetched' => mktime()));
 }
 
-
 /**
- * Given a record in the {blog_external} table, checks the blog's URL
- * for new entries not yet copied into Moodle.
- *
- * @param object $external_blog
- * @return void
+ * Given an external blog object, deletes all related blog entries from the post table.
+ * NOTE: The external blog's id is saved as post.content, a field that is not oterhwise used by blog entries.
+ * @param object $externablog
  */
-function blog_fetch_external_entries($external_blog) {
-    global $CFG, $DB;
-    require_once($CFG->libdir . '/simplepie/moodle_simplepie.php');
-
-    if (!blog_is_valid_url($external_blog->url)) {
-        return null;
-    }
-
-    $rss = new moodle_simplepie($external_blog->url);
-
-    if (empty($rss->data)) {
-        return null;
-    }
-
-    foreach ($rss->get_items() as $entry) {
-        $params = array('userid' => $external_blog->userid,
-                        'module' => 'blog',
-                        'uniquehash' => $entry->get_permalink(),
-                        'publishstate' => 'site',
-                        'format' => FORMAT_HTML);
-
-        if (!$DB->record_exists('blog_entries', $params)) {
-            $params['subject']      = $entry->get_title();
-            $params['summary']      = $entry->get_description();
-            $params['created']      = $entry->get_date('U');
-            $params['lastmodified'] = $entry->get_date('U');
-
-            $id = $DB->insert_record('blog_entries', $params);
-
-            // Set tags
-            if ($tags = tag_get_tags_array('blog_external', $external_blog->id)) {
-                tag_set('blog_entries', $id, $tags);
-            }
-        }
-    }
-
-    $DB->update_record('blog_external', array('id' => $external_blog->id, 'timefetched' => mktime()));
+function blog_delete_external_entries($externalblog) {
+    global $DB;
+    require_capability('moodle/blog:manageexternal', get_context_instance(CONTEXT_SYSTEM));
+    $DB->delete_records('post', array('content' => $externalblog->id, 'module' => 'blog_external'));
 }
 
 /**
@@ -367,9 +271,15 @@
 /**
  * This function encapsulates all the logic behind the complex
  * navigation, titles and headings of the blog listing page, depending
- * on URL params. It builds and returns an array containing:
+ * on URL params. It looks at URL params and at the current context level.
+ * It builds and returns an array containing:
+ *
+ * 1. heading: The heading displayed above the blog entries
+ * 2. stradd:  The text to be used as the "Add entry" link
+ * 3. strview: The text to be used as the "View entries" link
+ * 4. url:     The moodle_url object used as the base for add and view links
+ * 5. filters: An array of parameters used to filter blog listings. Used by index.php and the Recent blogs block
  *
- * 1. The heading displayed above the blog entries
  * All other variables are set directly in $PAGE
  *
  * It uses the current URL to build these variables.
@@ -392,196 +302,271 @@
     $action   = optional_param('action', null, PARAM_ALPHA);
     $confirm  = optional_param('confirm', false, PARAM_BOOL);
 
-    $headers = array('title' => '', 'heading' => '', 'cm' => null);
+    // Ignore userid when action == add
+    if ($action == 'add' && $userid) {
+        unset($userid);
+        $PAGE->url->remove_params(array('userid'));
+    } else if ($action == 'add' && $entryid) {
+        unset($entryid);
+        $PAGE->url->remove_params(array('entryid'));
+    }
+
+    $headers = array('title' => '', 'heading' => '', 'cm' => null, 'filters' => array());
 
-    $blog_url = new moodle_url($CFG->wwwroot . '/blog/index.php');
-    $site = $DB->get_record('course', array('id' => SITEID));
+    $blogurl = new moodle_url($CFG->wwwroot . '/blog/index.php');
+
+    try {
+        $contexturl = blog_get_context_url();
+
+        // Look at the context URL, it may have additional params that are not in the current URL
+        if (!$blogurl->compare($contexturl)) {
+            $blogurl = $contexturl;
+            if (empty($courseid)) {
+                $courseid = $blogurl->param('courseid');
+            }
+            if (empty($modid)) {
+                $modid = $blogurl->param('modid');
+            }
+        }
+    } catch (coding_exception $e) {
+        // Do nothing, we can safely ignore this exception, it means the page context is not yet set, so we don't need to examine it
+    }
 
+    $headers['stradd'] = get_string('addnewentry', 'blog');
+    $headers['strview'] = null;
+
+    $site = $DB->get_record('course', array('id' => SITEID));
+    $sitecontext = get_context_instance(CONTEXT_SYSTEM);
     // Common Lang strings
     $strparticipants = get_string("participants");
     $strblogentries  = get_string("blogentries", 'blog');
 
     // Prepare record objects as needed
     if (!empty($courseid)) {
+        $headers['filters']['course'] = $courseid;
         $course = $DB->get_record('course', array('id' => $courseid));
     }
 
     if (!empty($userid)) {
+        $headers['filters']['user'] = $userid;
         $user = $DB->get_record('user', array('id' => $userid));
     }
 
     if (!empty($groupid)) { // groupid always overrides courseid
+        $headers['filters']['group'] = $groupid;
         $group = $DB->get_record('groups', array('id' => $groupid));
         $course = $DB->get_record('course', array('id' => $group->courseid));
     }
 
-    if (!empty($modid)) { // modid always overrides courseid, so the $course object may be reset here
+    if (!empty($modid) && $CFG->useblogassociations && has_capability('moodle/blog:associatemodule', $sitecontext)) { // modid always overrides courseid, so the $course object may be reset here
+        $headers['filters']['module'] = $modid;
         // A groupid param may conflict with this coursemod's courseid. Ignore groupid in that case
-        $course_id = $DB->get_field('course_modules', 'course', array('id'=>$modid));
-        $course = $DB->get_record('course', array('id' => $course_id));
+        $courseid = $DB->get_field('course_modules', 'course', array('id'=>$modid));
+        $course = $DB->get_record('course', array('id' => $courseid));
         $cm = $DB->get_record('course_modules', array('id' => $modid));
         $cm->modname = $DB->get_field('modules', 'name', array('id' => $cm->module));
         $cm->name = $DB->get_field($cm->modname, 'name', array('id' => $cm->instance));
         $cm->context = get_context_instance(CONTEXT_MODULE, $modid);
+        $a->type = get_string('modulename', $cm->modname);
         $PAGE->set_cm($cm, $course);
+        $headers['stradd'] = get_string('blogaboutthis', 'blog', $a);
+        $headers['strview'] = get_string('viewallmodentries', 'blog', $a);
     }
 
-    // Case 0: No entry, mod, course or user params: all site entries to be shown (filtered by search and tag/tagid)
-    if (empty($entryid) && empty($modid) && empty($courseid) && empty($userid)) {
-        $PAGE->navbar->add($strblogentries, $blog_url);
+    // Case 1: No entry, mod, course or user params: all site entries to be shown (filtered by search and tag/tagid)
+    // Note: if action is set to 'add' or 'edit', we do this at the end
+    if (empty($entryid) && empty($modid) && empty($courseid) && empty($userid) && !in_array($action, array('edit', 'add'))) {
+        $PAGE->navbar->add($strblogentries, $blogurl);
         $PAGE->set_title("$site->shortname: " . get_string('blog', 'blog'));
         $PAGE->set_heading("$site->shortname: " . get_string('blog', 'blog'));
         $headers['heading'] = get_string('siteblog', 'blog');
+        // $headers['strview'] = get_string('viewsiteentries', 'blog');
     }
 
-    // Case 1: only entryid is requested, ignore all other filters. courseid is used to give more contextual information
-    // TODO Blog entries link has entryid instead of userid
+    // Case 2: only entryid is requested, ignore all other filters. courseid is used to give more contextual information
     if (!empty($entryid)) {
-        $sql = 'SELECT u.* FROM {user} u, {blog_entries} p WHERE p.id = ? AND p.userid = u.id';
+        $headers['filters']['entry'] = $entryid;
+        $sql = 'SELECT u.* FROM {user} u, {post} p WHERE p.id = ? AND p.userid = u.id';
         $user = $DB->get_record_sql($sql, array($entryid));
-        $entry = $DB->get_record('blog_entries', array('id' => $entryid));
+        $entry = $DB->get_record('post', array('id' => $entryid));
 
-        $blog_url->param('userid', $user->id);
+        $blogurl->param('userid', $user->id);
 
         if (!empty($course)) {
             $mycourseid = $course->id;
-            $blog_url->param('courseid', $mycourseid);
+            $blogurl->param('courseid', $mycourseid);
         } else {
             $mycourseid = $site->id;
         }
 
         $PAGE->navbar->add($strparticipants, "$CFG->wwwroot/user/index.php?id=$mycourseid");
         $PAGE->navbar->add(fullname($user), "$CFG->wwwroot/user/view.php?id=$user->id");
-        $PAGE->navbar->add($strblogentries, $blog_url);
-        $blog_url->param('entryid', $entryid);
-        $blog_url->remove_params('userid');
-        $PAGE->navbar->add($entry->subject, $blog_url);
+        $PAGE->navbar->add($strblogentries, $blogurl);
+
+        $blogurl->remove_params('userid');
+        $PAGE->navbar->add($entry->subject, $blogurl);
 
         $PAGE->set_title("$site->shortname: " . fullname($user) . ": $entry->subject");
         $PAGE->set_heading("$site->shortname: " . fullname($user) . ": $entry->subject");
         $headers['heading'] = get_string('blogentrybyuser', 'blog', fullname($user));
 
         // We ignore tag and search params
-        if (empty($action)) {
+        if (empty($action) || !$CFG->useblogassociations) {
+            $headers['url'] = $blogurl;
             return $headers;
         }
     }
 
-    // Case 2: A user's blog entries
-    if (!empty($userid) && empty($modid) && empty($courseid) && empty($entryid)) {
-        $blog_url->param('userid', $userid);
+    // Case 3: A user's blog entries
+    if (!empty($userid) && empty($entryid) && ((empty($courseid) && empty($modid)) || !$CFG->useblogassociations)) {
+        $blogurl->param('userid', $userid);
         $PAGE->navbar->add($strparticipants, "$CFG->wwwroot/user/index.php?id=$site->id");
         $PAGE->navbar->add(fullname($user), "$CFG->wwwroot/user/view.php?id=$user->id");
-        $PAGE->navbar->add($strblogentries, $blog_url);
+        $PAGE->navbar->add($strblogentries, $blogurl);
         $PAGE->set_title("$site->shortname: " . fullname($user) . ": " . get_string('blog', 'blog'));
         $PAGE->set_heading("$site->shortname: " . fullname($user) . ": " . get_string('blog', 'blog'));
         $headers['heading'] = get_string('userblog', 'blog', fullname($user));
+        $headers['strview'] = get_string('viewuserentries', 'blog');
+
+    } else
 
+    // Case 4: No blog associations, no userid
+    if (!$CFG->useblogassociations && empty($userid) && !in_array($action, array('edit', 'add'))) {
+        $PAGE->navbar->add($strblogentries, $blogurl);
+        $PAGE->set_title("$site->shortname: " . get_string('blog', 'blog'));
+        $PAGE->set_heading("$site->shortname: " . get_string('blog', 'blog'));
+        $headers['heading'] = get_string('siteblog', 'blog');
     } else
 
-    // Case 3: Blog entries associated with an activity by a specific user (courseid ignored)
+    // Case 5: Blog entries associated with an activity by a specific user (courseid ignored)
     if (!empty($userid) && !empty($modid) && empty($entryid)) {
-        $blog_url->param('userid', $userid);
-        $blog_url->param('modid', $modid);
+        $blogurl->param('userid', $userid);
+        $blogurl->param('modid', $modid);
 
         // Course module navigation is handled by build_navigation as the second param
         $headers['cm'] = $cm;
         $PAGE->navbar->add(fullname($user), "$CFG->wwwroot/user/view.php?id=$user->id");
-        $PAGE->navbar->add($strblogentries, $blog_url);
+        $PAGE->navbar->add($strblogentries, $blogurl);
 
         $PAGE->set_title("$site->shortname: $cm->name: " . fullname($user) . ': ' . get_string('blogentries', 'blog'));
         $PAGE->set_heading("$site->shortname: $cm->name: " . fullname($user) . ': ' . get_string('blogentries', 'blog'));
 
         $a->user = fullname($user);
         $a->mod = $cm->name;
+        $a->type = get_string('modulename', $cm->modname);
         $headers['heading'] = get_string('blogentriesbyuseraboutmodule', 'blog', $a);
+        $headers['stradd'] = get_string('blogaboutthis', 'blog', $a);
+        $headers['strview'] = get_string('viewallmodentries', 'blog', $a);
     } else
 
-    // Case 4: Blog entries associated with a course by a specific user
+    // Case 6: Blog entries associated with a course by a specific user
     if (!empty($userid) && !empty($courseid) && empty($modid) && empty($entryid)) {
-        $blog_url->param('userid', $userid);
-        $blog_url->param('courseid', $courseid);
+        $blogurl->param('userid', $userid);
+        $blogurl->param('courseid', $courseid);
 
         $PAGE->navbar->add($strparticipants, "$CFG->wwwroot/user/index.php?id=$course->id");
         $PAGE->navbar->add(fullname($user), "$CFG->wwwroot/user/view.php?id=$user->id");
-        $PAGE->navbar->add($strblogentries, $blog_url);
+        $PAGE->navbar->add($strblogentries, $blogurl);
 
         $PAGE->set_title("$site->shortname: $course->shortname: " . fullname($user) . ': ' . get_string('blogentries', 'blog'));
         $PAGE->set_heading("$site->shortname: $course->shortname: " . fullname($user) . ': ' . get_string('blogentries', 'blog'));
 
         $a->user = fullname($user);
         $a->course = $course->fullname;
+        $a->type = get_string('course');
         $headers['heading'] = get_string('blogentriesbyuseraboutcourse', 'blog', $a);
+        $headers['stradd'] = get_string('blogaboutthis', 'blog', $a);
+        $headers['strview'] = get_string('viewblogentries', 'blog', $a);
+
+        // Remove the userid from the URL to inform the blog_menu block correctly
+        $blogurl->remove_params(array('userid'));
     } else
 
-    // Case 5: Blog entries by members of a group, associated with that group's course
+    // Case 7: Blog entries by members of a group, associated with that group's course
     if (!empty($groupid) && empty($modid) && empty($entryid)) {
-        $blog_url->param('courseid', $course->id);
+        $blogurl->param('courseid', $course->id);
 
-        $PAGE->navbar->add($strblogentries, $blog_url);
-        $blog_url->remove_params(array('courseid'));
-        $blog_url->param('groupid', $groupid);
-        $PAGE->navbar->add($group->name, $blog_url);
+        $PAGE->navbar->add($strblogentries, $blogurl);
+        $blogurl->remove_params(array('courseid'));
+        $blogurl->param('groupid', $groupid);
+        $PAGE->navbar->add($group->name, $blogurl);
 
         $PAGE->set_title("$site->shortname: $course->shortname: " . get_string('blogentries', 'blog') . ": $group->name");
         $PAGE->set_heading("$site->shortname: $course->shortname: " . get_string('blogentries', 'blog') . ": $group->name");
 
         $a->group = $group->name;
         $a->course = $course->fullname;
+        $a->type = get_string('course');
         $headers['heading'] = get_string('blogentriesbygroupaboutcourse', 'blog', $a);
+        $headers['stradd'] = get_string('blogaboutthis', 'blog', $a);
+        $headers['strview'] = get_string('viewblogentries', 'blog', $a);
     } else
 
-    // Case 6: Blog entries by members of a group, associated with an activity in that course
+    // Case 8: Blog entries by members of a group, associated with an activity in that course
     if (!empty($groupid) && !empty($modid) && empty($entryid)) {
         $headers['cm'] = $cm;
-        $blog_url->param('modid', $modid);
-        $PAGE->navbar->add($strblogentries, $blog_url);
+        $blogurl->param('modid', $modid);
+        $PAGE->navbar->add($strblogentries, $blogurl);
 
-        $blog_url->param('groupid', $groupid);
-        $PAGE->navbar->add($group->name, $blog_url);
+        $blogurl->param('groupid', $groupid);
+        $PAGE->navbar->add($group->name, $blogurl);
 
         $PAGE->set_title("$site->shortname: $course->shortname: $cm->name: " . get_string('blogentries', 'blog') . ": $group->name");
         $PAGE->set_heading("$site->shortname: $course->shortname: $cm->name: " . get_string('blogentries', 'blog') . ": $group->name");
 
         $a->group = $group->name;
         $a->mod = $cm->name;
+        $a->type = get_string('modulename', $cm->modname);
         $headers['heading'] = get_string('blogentriesbygroupaboutmodule', 'blog', $a);
+        $headers['stradd'] = get_string('blogaboutthis', 'blog', $a);
+        $headers['strview'] = get_string('viewallmodentries', 'blog', $a);
 
     } else
 
-    // Case 7: All blog entries associated with an activity
+    // Case 9: All blog entries associated with an activity
     if (!empty($modid) && empty($userid) && empty($groupid) && empty($entryid)) {
         $PAGE->set_cm($cm, $course);
-        $blog_url->param('modid', $modid);
-        $PAGE->navbar->add($strblogentries, $blog_url);
+        $blogurl->param('modid', $modid);
+        $PAGE->navbar->add($strblogentries, $blogurl);
         $PAGE->set_title("$site->shortname: $course->shortname: $cm->name: " . get_string('blogentries', 'blog'));
         $PAGE->set_heading("$site->shortname: $course->shortname: $cm->name: " . get_string('blogentries', 'blog'));
         $headers['heading'] = get_string('blogentriesabout', 'blog', $cm->name);
+        $a->type = get_string('modulename', $cm->modname);
+        $headers['stradd'] = get_string('blogaboutthis', 'blog', $a);
+        $headers['strview'] = get_string('viewallmodentries', 'blog', $a);
     } else
 
-    // Case 8: All blog entries associated with a course
+    // Case 10: All blog entries associated with a course
     if (!empty($courseid) && empty($userid) && empty($groupid) && empty($modid) && empty($entryid)) {
-        $blog_url->param('courseid', $courseid);
-        $PAGE->navbar->add($strblogentries, $blog_url);
+        $blogurl->param('courseid', $courseid);
+        $PAGE->navbar->add($strblogentries, $blogurl);
         $PAGE->set_title("$site->shortname: $course->shortname: " . get_string('blogentries', 'blog'));
         $PAGE->set_heading("$site->shortname: $course->shortname: " . get_string('blogentries', 'blog'));
+        $a->type = get_string('course');
         $headers['heading'] = get_string('blogentriesabout', 'blog', $course->fullname);
+        $headers['stradd'] = get_string('blogaboutthis', 'blog', $a);
+        $headers['strview'] = get_string('viewblogentries', 'blog', $a);
+        $blogurl->remove_params(array('userid'));
     }
 
+    if (!in_array($action, array('edit', 'add'))) {
-    // Append Tag info
-    if (!empty($tagid)) {
+        // Append Tag info
+        if (!empty($tagid)) {
-        $blog_url->param('tagid', $tagid);
+            $headers['filters']['tag'] = $tagid;
+            $blogurl->param('tagid', $tagid);
-        $tagrec = $DB->get_record('tag', array('id'=>$tagid));
+            $tagrec = $DB->get_record('tag', array('id'=>$tagid));
-        $PAGE->navbar->add($tagrec->name, $blog_url);
+            $PAGE->navbar->add($tagrec->name, $blogurl);
-    } elseif (!empty($tag)) {
+        } elseif (!empty($tag)) {
-        $blog_url->param('tag', $tag);
-        $PAGE->navbar->add(get_string('tagparam', 'blog', $tag), $blog_url);
+            $blogurl->param('tag', $tag);
+            $PAGE->navbar->add(get_string('tagparam', 'blog', $tag), $blogurl);
-    }
+        }
 
-    // Append Search info
-    if (!empty($search)) {
+        // Append Search info
+        if (!empty($search)) {
-        $blog_url->param('search', $search);
-        $PAGE->navbar->add(get_string('searchterm', 'blog', $search), $blog_url->out());
+            $headers['filters']['search'] = $search;
+            $blogurl->param('search', $search);
+            $PAGE->navbar->add(get_string('searchterm', 'blog', $search), $blogurl->out());
+        }
     }
 
     // Append edit mode info
@@ -592,15 +577,25 @@
             }
             $PAGE->navbar->add($strparticipants, "$CFG->wwwroot/user/index.php?id=$site->id");
             $PAGE->navbar->add(fullname($user), "$CFG->wwwroot/user/view.php?id=$user->id");
+            $blogurl->param('userid', $user->id);
+            $PAGE->navbar->add($strblogentries, $blogurl);
         }
         $PAGE->navbar->add(get_string('addnewentry', 'blog'));
     } else if (!empty($action) && $action == 'edit') {
         $PAGE->navbar->add(get_string('editentry', 'blog'));
     }
 
+    if (empty($headers['url'])) {
+        $headers['url'] = $blogurl;
+    }
     return $headers;
 }
 
+/**
+ * Function used by the navigation system to provide links to blog preferences and external blogs.
+ * @param object $settingsnav The settings_navigation object
+ * @return navigation key
+ */
 function blog_extend_settings_navigation($settingsnav) {
     global $USER, $PAGE, $FULLME, $CFG, $DB, $OUTPUT;
     $blogkey = $settingsnav->add(get_string('blogadministration', 'blog'));
@@ -608,9 +603,25 @@
     $blog->forceopen = true;
 
     $blog->add(get_string('preferences', 'blog'), new moodle_url('preferences.php'), navigation_node::TYPE_SETTING);
-    if ($CFG->useexternalblogs && $CFG->maxexternalblogsperuser > 0) {
-        $blog->add(get_string('externalblogs', 'blog'), new moodle_url('external.php'), navigation_node::TYPE_SETTING);
+
+    if ($CFG->useexternalblogs && $CFG->maxexternalblogsperuser > 0 && has_capability('moodle/blog:manageexternal', get_context_instance(CONTEXT_SYSTEM))) {
+        $blog->add(get_string('externalblogs', 'blog'), new moodle_url('external_blogs.php'), navigation_node::TYPE_SETTING);
     }
 
     return $blogkey;
 }
+
+/**
+ * Shortcut function for getting a count of blog entries associated with a course or a module
+ * @param int $courseid The ID of the course
+ * @param int $cmid The ID of the course_modules
+ * @return string The number of associated entries
+ */
+function blog_get_associated_count($courseid, $cmid=null) {
+    global $DB;
+    $context = get_context_instance(CONTEXT_COURSE, $courseid);
+    if ($cmid) {
+        $context = get_context_instance(CONTEXT_MODULE, $cmid);
+    }
+    return $DB->count_records('blog_association', array('contextid' => $context->id));
+}
Index: blog/locallib.php
=========================================================
--- blog/locallib.php	(revision 1.7)
+++ blog/locallib.php	Wed Oct 28 16:42:23 WST 2009
@@ -30,7 +30,7 @@
  * Blog_entry class. Represents an entry in a user's blog. Contains all methods for managing this entry.
  * This class does not contain any HTML-generating code. See blog_listing sub-classes for such code.
  * This class follows the Object Relational Mapping technique, its member variables being mapped to
- * the fields of the blog_entries table.
+ * the fields of the post table.
  *
  * @package    moodlecore
  * @subpackage blog
@@ -43,13 +43,19 @@
     public $userid;
     public $subject;
     public $summary;
+    public $rating = 0;
+    public $attachment;
     public $publishstate;
 
     // Locked Database fields (Don't touch these)
+    public $courseid = 0;
+    public $groupid = 0;
+    public $module = 'blog';
+    public $moduleid = 0;
+    public $coursemoduleid = 0;
     public $content;
     public $format = 1;
-    public $summaryformat = 1;
-    public $permalink = '';
+    public $uniquehash = '';
     public $lastmodified;
     public $created;
     public $usermodified;
@@ -64,16 +70,16 @@
      *
      * @param mixed $idorparams A blog entry id if INT, or data for a new entry if array
      */
-    public function __construct($idorparams=null, $form=null) {
+    public function __construct($id=null, $params=null, $form=null) {
         global $DB, $PAGE;
 
-        if (!empty($idorparams) && !is_array($idorparams) && !is_object($idorparams)) {
-            $object = $DB->get_record('blog_entries', array('id' => $idorparams));
+        if (!empty($id)) {
+            $object = $DB->get_record('post', array('id' => $id));
             foreach ($object as $var => $val) {
                 $this->$var = $val;
             }
-        } else if (!empty($idorparams) && (is_array($idorparams) || is_object($idorparams))) {
-            foreach ($idorparams as $var => $val) {
+        } else if (!empty($params) && (is_array($params) || is_object($params))) {
+            foreach ($params as $var => $val) {
                 $this->$var = $val;
             }
         }
@@ -91,7 +97,6 @@
 
         global $USER, $CFG, $COURSE, $DB, $OUTPUT, $PAGE;
 
-
         $user = $DB->get_record('user', array('id'=>$this->userid));
         // Comments
         $cmt = new stdClass();
@@ -99,10 +104,10 @@
         $cmt->area = 'format_blog';
         $cmt->itemid = $this->id;
         $options->comments = $cmt;
+        $this->summary = file_rewrite_pluginfile_urls($this->summary, 'pluginfile.php', SYSCONTEXTID, 'blog_post', $this->id);
 
-        $template['body'] = format_text($this->summary, $this->format, $options);
+        $template['body'] = format_text($this->summary, $this->summaryformat, $options);
         $template['title'] = '<a id="b'. s($this->id) .'" />';
-        //enclose the title in nolink tags so that moodle formatting doesn't autolink the text
         $template['title'] .= '<span class="nolink">'. format_string($this->subject) .'</span>';
         $template['userid'] = $user->id;
         $template['author'] = fullname($user);
@@ -117,7 +122,7 @@
         $stredit = get_string('edit');
         $strdelete = get_string('delete');
 
-        //check to see if the entry is unassociated with group/course level access
+        // Check to see if the entry is unassociated with group/course level access
         $unassociatedentry = false;
         if (!empty($CFG->useblogassociations) && ($this->publishstate == 'group' || $this->publishstate == 'course')) {
             if (!$DB->record_exists('blog_association', array('blogid' => $this->id))) {
@@ -125,7 +130,7 @@
             }
         }
 
-        /// Start printing of the blog
+        // Start printing of the blog
         $table = new html_table();
         $table->cellspacing = 0;
         $table->add_classes('forumpost blog_entry blog'. ($unassociatedentry ? 'draft' : $template['publishstate']));
@@ -149,10 +154,18 @@
 
         $topiccell->text .= get_string('bynameondate', 'forum', $by);
         $topiccell->text .= $OUTPUT->container_end();
+
+        if ($this->uniquehash && $this->content) {
+            if ($externalblog = $DB->get_record('blog_external', array('id' => $this->content))) {
+                $urlparts = parse_url($externalblog->url);
+                $topiccell->text .= $OUTPUT->container(get_string('retrievedfrom', 'blog') . $OUTPUT->link(html_link::make($urlparts['scheme'].'://'.$urlparts['host'], $externalblog->name)), 'externalblog');
+            }
+        }
+
         $topiccell->header = false;
         $table->head[] = $topiccell;
 
-    /// Actual content
+        // Actual content
         $mainrow = new html_table_row();
 
         $leftsidecell = new html_table_cell();
@@ -162,23 +175,20 @@
         $contentcell = new html_table_cell();
         $contentcell->add_class('content');
 
-        if ($this->attachment) {
-            $attachedimages = $OUTPUT->container($this->print_attachments(), 'attachments');
+        $attachedimages = $OUTPUT->container($this->print_attachments(), 'attachments');
-        } else {
-            $attachedimages = '';
-        }
 
-        //retrieve associations in case they're needed early
+        // retrieve associations in case they're needed early
         $blogassociations = $DB->get_records('blog_association', array('blogid' => $this->id));
-        //determine text for publish state
+
+        // determine text for publish state
         switch ($template['publishstate']) {
-            case BLOG_PUBLISHSTATE_DRAFT:
+            case 'draft':
                 $blogtype = get_string('publishtonoone', 'blog');
             break;
-            case BLOG_PUBLISHSTATE_SITE:
+            case 'site':
                 $blogtype = get_string('publishtosite', 'blog');
             break;
-            case BLOG_PUBLISHSTATE_PUBLIC:
+            case 'public':
                 $blogtype = get_string('publishtoworld', 'blog');
             break;
             default:
@@ -192,52 +202,62 @@
         $contentcell->text .= $template['body'];
         $contentcell->text .= $attachedimages;
 
-        // permalink is used as a link to an external blog
-        if (!empty($this->permalink) && blog_is_valid_url($this->permalink)) {
+        // Uniquehash is used as a link to an external blog
+        if (!empty($this->uniquehash)) {
             $contentcell->text .= $OUTPUT->container_start('externalblog');
-            $contentcell->text .= $OUTPUT->link(html_link::make($this->permalink, get_string('linktooriginalentry', 'blog')));
+            $contentcell->text .= $OUTPUT->link(html_link::make($this->uniquehash, get_string('linktooriginalentry', 'blog')));
             $contentcell->text .= $OUTPUT->container_end();
         }
 
         // Links to tags
+        $officialtags = tag_get_tags_csv('post', $this->id, TAG_RETURN_HTML, 'official');
+        $defaulttags = tag_get_tags_csv('post', $this->id, TAG_RETURN_HTML, 'default');
 
-        if (!empty($CFG->usetags) && ($blogtags = tag_get_tags_csv('blog_entries', $this->id)) ) {
+        if (!empty($CFG->usetags) && ($officialtags || $defaulttags) ) {
             $contentcell->text .= $OUTPUT->container_start('tags');
 
-            if ($blogtags) {
-                $contentcell->text .= get_string('tags', 'tag') .': '. $blogtags;
+            if ($officialtags) {
+                $contentcell->text .= get_string('tags', 'tag') .': '. $OUTPUT->container($officialtags, 'officialblogtags');
+                if ($defaulttags) {
+                    $contentcell->text .=  ', ';
-            }
+                }
+            }
+            $contentcell->text .=  $defaulttags;
             $contentcell->text .= $OUTPUT->container_end();
         }
 
-        //add associations
+        // Add associations
         if (!empty($CFG->useblogassociations) && $blogassociations) {
             $contentcell->text .= $OUTPUT->container_start('tags');
             $assocstr = '';
             $hascourseassocs = false;
+            $assoctype = '';
 
-            foreach ($blogassociations as $assocrec) {  //first find and show the associated course
-                $context_rec = $DB->get_record('context', array('id' => $assocrec->contextid));
-                if ($context_rec->contextlevel ==  CONTEXT_COURSE) {
+            // First find and show the associated course
+            foreach ($blogassociations as $assocrec) {
+                $contextrec = $DB->get_record('context', array('id' => $assocrec->contextid));
+                if ($contextrec->contextlevel ==  CONTEXT_COURSE) {
                     $associcon = new moodle_action_icon();
-                    $associcon->link->url = new moodle_url($CFG->wwwroot.'/course/view.php', array('id' => $context_rec->instanceid));
+                    $associcon->link->url = new moodle_url($CFG->wwwroot.'/course/view.php', array('id' => $contextrec->instanceid));
                     $associcon->image->src = $OUTPUT->old_icon_url('i/course');
-                    $associcon->linktext = $DB->get_field('course', 'shortname', array('id' => $context_rec->instanceid));
+                    $associcon->linktext = $DB->get_field('course', 'shortname', array('id' => $contextrec->instanceid));
                     $assocstr .= $OUTPUT->action_icon($associcon);
                     $hascourseassocs = true;
+                    $assoctype = get_string('course');
                 }
             }
 
-            foreach ($blogassociations as $assocrec) {  //now show each mod association
-                $context_rec = $DB->get_record('context', array('id' => $assocrec->contextid));
+            // Now show mod association
+            foreach ($blogassociations as $assocrec) {
+                $contextrec = $DB->get_record('context', array('id' => $assocrec->contextid));
 
-                if ($context_rec->contextlevel ==  CONTEXT_MODULE) {
+                if ($contextrec->contextlevel ==  CONTEXT_MODULE) {
                     if ($hascourseassocs) {
                         $assocstr .= ', ';
                         $hascourseassocs = false;
                     }
 
-                    $modinfo = $DB->get_record('course_modules', array('id' => $context_rec->instanceid));
+                    $modinfo = $DB->get_record('course_modules', array('id' => $contextrec->instanceid));
                     $modname = $DB->get_field('modules', 'name', array('id' => $modinfo->module));
 
                     $associcon = new moodle_action_icon();
@@ -246,11 +266,12 @@
                     $associcon->linktext = $DB->get_field($modname, 'name', array('id' => $modinfo->instance));
                     $assocstr .= $OUTPUT->action_icon($associcon);
                     $assocstr .= ', ';
+                    $assoctype = get_string('modulename', $modname);
 
                 }
             }
             $assocstr = substr($assocstr, 0, -2);
-            $contentcell->text .= get_string('associations', 'blog') . ': '. $assocstr;
+            $contentcell->text .= get_string('associated', 'blog', $assoctype) . ': '. $assocstr;
 
             $contentcell->text .= $OUTPUT->container_end();
         }
@@ -263,7 +284,7 @@
 
         $contentcell->text .= $OUTPUT->container_start('commands');
 
-        if (blog_user_can_edit_entry($this)) {
+        if (blog_user_can_edit_entry($this) && empty($this->uniquehash)) {
             $contentcell->text .= $OUTPUT->link(html_link::make(new moodle_url($CFG->wwwroot.'/blog/edit.php', array('action' => 'edit', 'entryid' => $this->id)), $stredit)) . ' | ';
             $contentcell->text .= $OUTPUT->link(html_link::make(new moodle_url($CFG->wwwroot.'/blog/edit.php', array('action' => 'delete', 'entryid' => $this->id)), $strdelete)) . ' | ';
         }
@@ -314,20 +335,18 @@
         $this->created      = time();
 
         // Insert the new blog entry.
-        $this->id = $DB->insert_record('blog_entries', $this);
-
-        // TODO File handling
+        if ($this->id = $DB->insert_record('post', $this)) {
 
-        // Update tags.
-        $this->add_tags_info();
+            // Update tags.
+            $this->add_tags_info();
 
-        if (!empty($CFG->useblogassociations)) {
-            $this->add_associations();
-            add_to_log(SITEID, 'blog', 'add', 'index.php?userid='.$this->userid.'&entryid='.$this->id, $this->subject);
+            if (!empty($CFG->useblogassociations)) {
+                $this->add_associations();
+                add_to_log(SITEID, 'blog', 'add', 'index.php?userid='.$this->userid.'&entryid='.$this->id, $this->subject);
+            }
 
+            tag_set('post', $this->id, $this->tags);
         }
-
-        tag_set('blog_entries', $this->id, $this->tags);
     }
 
     /**
@@ -336,28 +355,31 @@
      * @param mform $form Used for attachments
      * @return void
      */
-    public function edit($params=array(), $form=null) {
+    public function edit($params=array(), $form=null, $summaryoptions=array(), $attachmentoptions=array()) {
         global $CFG, $USER, $DB, $PAGE;
 
+        $sitecontext = get_context_instance(CONTEXT_SYSTEM);
+        $entry = $this;
+
         $this->form = $form;
         foreach ($params as $var => $val) {
-            $this->$var = $val;
+            $entry->$var = $val;
         }
 
-        $this->summary = file_save_draft_area_files($params->summary['itemid'], $PAGE->context->id, 'blog_post', $this->id, array('subdirs'=>true), $params->summary['text']);
+        $entry = file_postupdate_standard_editor($entry, 'summary', $summaryoptions, $sitecontext, 'blog_post', $entry->id);
+        $entry = file_postupdate_standard_filemanager($entry, 'attachment', $attachmentoptions, $sitecontext, 'blog_attachment', $entry->id);
 
         if (!empty($CFG->useblogassociations)) {
-            $this->add_associations();
+            $entry->add_associations();
         }
 
-        $this->lastmodified = time();
+        $entry->lastmodified = time();
-        
+
-        // TODO Handle attachments
         // Update record
-        $DB->update_record('blog_entries', $this);
-        tag_set('blog_entries', $this->id, $this->tags);
+        $DB->update_record('post', $entry);
+        tag_set('post', $entry->id, $entry->tags);
 
-        add_to_log(SITEID, 'blog', 'update', 'index.php?userid='.$USER->id.'&entryid='.$this->id, $this->subject);
+        add_to_log(SITEID, 'blog', 'update', 'index.php?userid='.$USER->id.'&entryid='.$entry->id, $entry->subject);
     }
 
     /**
@@ -372,8 +394,8 @@
 
         $this->delete_attachments();
 
-        $DB->delete_records('blog_entries', array('id' => $this->id));
-        tag_set('blog_entries', $this->id, array());
+        $DB->delete_records('post', array('id' => $this->id));
+        tag_set('post', $this->id, array());
 
         add_to_log(SITEID, 'blog', 'delete', 'index.php?userid='. $this->userid, 'deleted blog entry with entry id# '. $this->id);
     }
@@ -382,23 +404,17 @@
      * function to add all context associations to an entry
      * @param int entry - data object processed to include all 'entry' fields and extra data from the edit_form object
      */
-    public function add_associations() {
+    public function add_associations($action='add') {
         global $DB, $USER;
 
-        $allowaddcourseassoc = true;
-
         $this->remove_associations();
 
         if (!empty($this->courseassoc)) {
-            $this->add_association($this->courseassoc);
-            $allowaddcourseassoc = false;
+            $this->add_association($this->courseassoc, $action);
         }
 
         if (!empty($this->modassoc)) {
-            foreach ($this->modassoc as $modid) {
-                $this->add_association($modid, $allowaddcourseassoc);
-                $allowaddcourseassoc = false;   //let the course be added the first time
-            }
+            $this->add_association($this->modassoc, $action);
         }
     }
 
@@ -406,13 +422,25 @@
      * add a single association for a blog entry
      * @param int contextid - id of context to associate with the blog entry
      */
-    public function add_association($contextid) {
-        global $DB;
+    public function add_association($contextid, $action='add') {
+        global $DB, $USER;
 
-        $assoc_object = new StdClass;
-        $assoc_object->contextid = $contextid;
-        $assoc_object->blogid = $this->id;
-        $DB->insert_record('blog_association', $assoc_object);
+        $assocobject = new StdClass;
+        $assocobject->contextid = $contextid;
+        $assocobject->blogid = $this->id;
+        $DB->insert_record('blog_association', $assocobject);
+
+        $context = get_context_instance_by_id($contextid);
+        $courseid = null;
+
+        if ($context->contextlevel == CONTEXT_COURSE) {
+            $courseid = $context->instanceid;
+            add_to_log($courseid, 'blog', $action, 'index.php?userid='.$this->userid.'&entryid='.$this->id, $this->subject);
+        } else if ($context->contextlevel == CONTEXT_MODULE) {
+            $cm = $DB->get_record('course_modules', array('id' => $context->instanceid));
+            $modulename = $DB->get_field('modules', 'name', array('id' => $cm->module));
+            add_to_log($cm->course, 'blog', $action, 'index.php?userid='.$this->userid.'&entryid='.$this->id, $this->subject, $cm->id, $this->userid);
+        }
     }
 
     /**
@@ -431,27 +459,26 @@
      */
     public function delete_attachments() {
         $fs = get_file_storage();
-        $fs->delete_area_files(SYSCONTEXTID, 'blog', $this->id);
+        $fs->delete_area_files(SYSCONTEXTID, 'blog_attachment', $this->id);
     }
 
     /**
      * if return=html, then return a html string.
      * if return=text, then return a text-only string.
      * otherwise, print HTML for non-images, and return image HTML
-     * TODO Fix this so that it displays attachments correctly
      *
      * @param bool $return Whether to return or print the generated code
      * @return void
      */
     public function print_attachments($return=false) {
-        global $CFG;
+        global $CFG, $OUTPUT;
 
         require_once($CFG->libdir.'/filelib.php');
 
         $fs = get_file_storage();
         $browser = get_file_browser();
 
-        $files = $fs->get_area_files(SYSCONTEXTID, 'blog', $this->id);
+        $files = $fs->get_area_files(SYSCONTEXTID, 'blog_attachment', $this->id);
 
         $imagereturn = "";
         $output = "";
@@ -464,26 +491,33 @@
             }
 
             $filename = $file->get_filename();
-            $ffurl    = file_encode_url($CFG->wwwroot.'/pluginfile.php', '/'.SYSCONTEXTID.'/blog/'.$this->id.'/'.$filename);
-            $type     = $file->get_mimetype();
-            $icon     = mimeinfo_from_type("icon", $type);
-            $type     = mimeinfo_from_type("type", $type);
+            $ffurl    = file_encode_url($CFG->wwwroot.'/pluginfile.php', '/'.SYSCONTEXTID.'/blog_attachment/'.$this->id.'/'.$filename);
+            $mimetype = $file->get_mimetype();
 
-            $image = "<img src=\"$CFG->pixpath/f/$icon\" class=\"icon\" alt=\"\" />";
+            $icon     = substr(mimeinfo_from_type("icon", $mimetype), 0, -4);
+            $type     = mimeinfo_from_type("type", $mimetype);
+
+            $image = new html_image();
+            $image->src = $OUTPUT->old_icon_url("/f/$icon");
+            $image->add_class('icon');
+            $image->alt = $filename;
 
             if ($return == "html") {
-                $output .= "<a href=\"$ffurl\">$image</a> ";
-                $output .= "<a href=\"$ffurl\">$filename</a><br />";
+                $output .= $OUTPUT->link(html_link::make($ffurl, $OUTPUT->image($image)));
+                $output .= $OUTPUT->link(html_link::make($ffurl, $filename));
 
             } else if ($return == "text") {
                 $output .= "$strattachment $filename:\n$ffurl\n";
 
             } else {
                 if (in_array($type, array('image/gif', 'image/jpeg', 'image/png'))) {    // Image attachments don't get printed as links
-                    $imagereturn .= "<br /><img src=\"$ffurl\" alt=\"\" />";
+                    $image = new html_image();
+                    $image->src = $ffurl;
+                    $image->alt = $filename;
+                    $imagereturn .= "<br />" . $OUTPUT->image($image);
                 } else {
-                    $imagereturn .= "<a href=\"$ffurl\">$image</a> ";
-                    $imagereturn .= filter_text("<a href=\"$ffurl\">$filename</a><br />");
+                    $imagereturn .= $OUTPUT->link(html_link::make($ffurl, $OUTPUT->image($image)));
+                    $imagereturn .= filter_text($OUTPUT->link(html_link::make($ffurl, $filename)));
                 }
             }
         }
@@ -513,7 +547,7 @@
             }
         }
 
-        tag_set('blog_entries', $this->id, $tags);
+        tag_set('post', $this->id, $tags);
     }
 
     /**
@@ -556,27 +590,27 @@
      */
     public function can_user_view($targetuserid) {
         global $CFG, $USER, $DB;
+        $sitecontext = get_context_instance(CONTEXT_SYSTEM);
 
-        if (empty($CFG->bloglevel)) {
-            return false; // blog system disabled
+        if (empty($CFG->bloglevel) || !has_capability('moodle/blog:view', $sitecontext)) {
+            return false; // blog system disabled or user has no blog view capability
         }
 
-        if (!empty($USER->id) and $USER->id == $targetuserid) {
+        if (!empty($USER->id) && $USER->id == $targetuserid) {
             return true; // can view own entries in any case
         }
 
-        $sitecontext = get_context_instance(CONTEXT_SYSTEM);
         if (has_capability('moodle/blog:manageentries', $sitecontext)) {
             return true; // can manage all entries
         }
 
         // coming for 1 entry, make sure it's not a draft
-        if ($this->publishstate == BLOG_PUBLISHSTATE_DRAFT) {
+        if ($this->publishstate == 'draft' && !has_capability('moodle/blog:viewdrafts', $sitecontext)) {
             return false;  // can not view draft of others
         }
 
         // coming for 1 entry, make sure user is logged in, if not a public blog
-        if ($this->publishstate != BLOG_PUBLISHSTATE_PUBLIC && !isloggedin()) {
+        if ($this->publishstate != 'public' && !isloggedin()) {
             return false;
         }
 
@@ -614,15 +648,15 @@
 
         // everyone gets draft access
         if ($CFG->bloglevel >= BLOG_USER_LEVEL) {
-            $options[BLOG_PUBLISHSTATE_DRAFT] = get_string('publishtonoone', 'blog');
+            $options['draft'] = get_string('publishtonoone', 'blog');
         }
 
         if ($CFG->bloglevel > BLOG_USER_LEVEL) {
-            $options[BLOG_PUBLISHSTATE_SITE] = get_string('publishtosite', 'blog');
+            $options['site'] = get_string('publishtosite', 'blog');
         }
 
         if ($CFG->bloglevel >= BLOG_GLOBAL_LEVEL) {
-            $options[BLOG_PUBLISHSTATE_PUBLIC] = get_string('publishtoworld', 'blog');
+            $options['public'] = get_string('publishtoworld', 'blog');
         }
 
         return $options;
@@ -681,8 +715,8 @@
         global $DB;
 
         if (empty($this->entries)) {
-            if ($sql_array = $this->get_entry_fetch_sql()) {
-                $this->entries = $DB->get_records_sql($sql_array['sql'], $sql_array['params'], $start, $limit);
+            if ($sqlarray = $this->get_entry_fetch_sql()) {
+                $this->entries = $DB->get_records_sql($sqlarray['sql'] . " LIMIT $start, $limit", $sqlarray['params']);
             } else {
                 return false;
             }
@@ -699,9 +733,9 @@
         }
 
         // The query used to locate blog entries is complicated.  It will be built from the following components:
-        $requiredfields = "b.*, u.firstname, u.lastname, u.email";  // the SELECT clause
-        $tables = array('b' => 'blog_entries', 'u' => 'user');   // components of the FROM clause (table_id => table_name)
-        $conditions = array('u.deleted = 0', 'b.userid = u.id');  // components of the WHERE clause (conjunction)
+        $requiredfields = "p.*, u.firstname, u.lastname, u.email";  // the SELECT clause
+        $tables = array('p' => 'post', 'u' => 'user');   // components of the FROM clause (table_id => table_name)
+        $conditions = array('u.deleted = 0', 'p.userid = u.id', '(p.module = \'blog\' OR p.module = \'blog_external\')');  // components of the WHERE clause (conjunction)
 
         // build up a clause for permission constraints
 
@@ -721,28 +755,28 @@
                 $assocexists = $DB->record_exists('blog_association', array());  //dont check association records if there aren't any
 
                 //begin permission sql clause
-                $permissionsql =  '(b.userid = ? ';
+                $permissionsql =  '(p.userid = ? ';
                 $params[] = $userid;
 
                 if ($CFG->bloglevel >= BLOG_SITE_LEVEL) { // add permission to view site-level entries
-                    $permissionsql .= " OR b.publishstate = " . BLOG_PUBLISHSTATE_SITE;
+                    $permissionsql .= " OR p.publishstate = 'site' ";
                 }
 
                 if ($CFG->bloglevel >= BLOG_GLOBAL_LEVEL) { // add permission to view global entries
-                    $permissionsql .= " OR b.publishstate = " . BLOG_PUBLISHSTATE_DRAFT;
+                    $permissionsql .= " OR p.publishstate = 'public' ";
                 }
 
                 $permissionsql .= ') ';   //close permissions sql clause
             } else {  // default is access to public entries
-                $permissionsql = "b.publishstate = " . BLOG_PUBLISHSTATE_PUBLIC;
+                $permissionsql = "p.publishstate = 'public'";
             }
             $conditions[] = $permissionsql;  //add permission constraints
         }
 
-        foreach ($this->filters as $type => $blog_filter) {
-            $conditions = array_merge($conditions, $blog_filter->conditions);
-            $params = array_merge($params, $blog_filter->params);
-            $tables = array_merge($tables, $blog_filter->tables);
+        foreach ($this->filters as $type => $blogfilter) {
+            $conditions = array_merge($conditions, $blogfilter->conditions);
+            $params = array_merge($params, $blogfilter->params);
+            $tables = array_merge($tables, $blogfilter->tables);
         }
 
         $tablessql = '';  // build up the FROM clause
@@ -772,8 +806,8 @@
 
         $morelink = '<br />&nbsp;&nbsp;';
 
-        if ($sql_array = $this->get_entry_fetch_sql(true)) {
-            $totalentries = $DB->count_records_sql($sql_array['sql'], $sql_array['params']);
+        if ($sqlarray = $this->get_entry_fetch_sql(true)) {
+            $totalentries = $DB->count_records_sql($sqlarray['sql'], $sqlarray['params']);
         } else {
             $totalentries = 0;
         }
@@ -781,6 +815,7 @@
         $entries = $this->get_entries($start, $limit);
         $pagingbar = moodle_paging_bar::make($totalentries, $page, $limit, $this->get_baseurl());
         $pagingbar->pagevar = 'blogpage';
+        $blogheaders = blog_get_headers();
 
         echo $OUTPUT->paging_bar($pagingbar);
 
@@ -795,25 +830,25 @@
             $userid = optional_param('userid', null, PARAM_INT);
 
             if (empty($userid) || (!empty($userid) && $userid == $USER->id)) {
-                $add_url = new moodle_url("$CFG->wwwroot/blog/edit.php");
-                $url_params = array('action' => 'add',
+                $addurl = new moodle_url("$CFG->wwwroot/blog/edit.php");
+                $urlparams = array('action' => 'add',
-                                    'userid' => $userid,
-                                    'courseid' => optional_param('courseid', null, PARAM_INT),
-                                    'groupid' => optional_param('groupid', null, PARAM_INT),
-                                    'modid' => optional_param('modid', null, PARAM_INT),
-                                    'tagid' => optional_param('tagid', null, PARAM_INT),
-                                    'tag' => optional_param('tag', null, PARAM_INT),
-                                    'search' => optional_param('search', null, PARAM_INT));
+                                   'userid' => $userid,
+                                   'courseid' => optional_param('courseid', null, PARAM_INT),
+                                   'groupid' => optional_param('groupid', null, PARAM_INT),
+                                   'modid' => optional_param('modid', null, PARAM_INT),
+                                   'tagid' => optional_param('tagid', null, PARAM_INT),
+                                   'tag' => optional_param('tag', null, PARAM_INT),
+                                   'search' => optional_param('search', null, PARAM_INT));
 
-                foreach ($url_params as $var => $val) {
+                foreach ($urlparams as $var => $val) {
                     if (empty($val)) {
-                        unset($url_params[$var]);
+                        unset($urlparams[$var]);
                     }
                 }
-                $add_url->params($url_params);
+                $addurl->params($urlparams);
 
                 $addlink = '<div class="addbloglink">';
-                $addlink .= '<a href="'.$add_url->out().'">'. get_string('addnewentry', 'blog').'</a>';
+                $addlink .= '<a href="'.$addurl->out().'">'. $blogheaders['stradd'].'</a>';
                 $addlink .= '</div>';
                 echo $addlink;
             }
@@ -823,8 +858,8 @@
             $count = 0;
 
             foreach ($entries as $entry) {
-                $blog_entry = new blog_entry($entry);
-                $blog_entry->print_html();
+                $blogentry = new blog_entry(null, $entry);
+                $blogentry->print_html();
                 $count++;
             }
 
@@ -879,9 +914,9 @@
 abstract class blog_filter {
     /**
      * An array of strings representing the available filter types for each blog_filter.
-     * @var array $available_types
+     * @var array $availabletypes
      */
-    public $available_types = array();
+    public $availabletypes = array();
 
     /**
      * The type of filter (for example, types of blog_filter_context are site, course and module)
@@ -946,9 +981,9 @@
                 break;
 
             default:
-                $class_name = "blog_filter_$type";
-                if (class_exists($class_name)) {
-                    return new $class_name($id, $type);
+                $classname = "blog_filter_$type";
+                if (class_exists($classname)) {
+                    return new $classname($id, $type);
                 }
         }
     }
@@ -974,7 +1009,7 @@
             $this->type = $type;
         }
 
-        $this->available_types = array('site' => get_string('site'), 'course' => get_string('course'), 'module' => get_string('module'));
+        $this->availabletypes = array('site' => get_string('site'), 'course' => get_string('course'), 'module' => get_string('module'));
 
         switch ($this->type) {
             case 'course': // Careful of site course!
@@ -983,7 +1018,7 @@
                     $this->overrides = array('site');
                     $context = get_context_instance(CONTEXT_COURSE, $this->id);
                     $this->tables['ba'] = 'blog_association';
-                    $this->conditions[] = 'b.id = ba.blogid';
+                    $this->conditions[] = 'p.id = ba.blogid';
                     $this->conditions[] = 'ba.contextid = '.$context->id;
                     break;
                 } else {
@@ -999,8 +1034,8 @@
 
                     $context = get_context_instance(CONTEXT_MODULE, $this->id);
                     $this->tables['ba'] = 'blog_association';
-                    $this->tables['b']  = 'blog_entries';
-                    $this->conditions = array('b.id = ba.blogid', 'ba.contextid = ?');
+                    $this->tables['p']  = 'post';
+                    $this->conditions = array('p.id = ba.blogid', 'ba.contextid = ?');
                     $this->params = array($context->id);
                 }
                 break;
@@ -1023,7 +1058,7 @@
      */
     public function __construct($id=null, $type='user') {
         global $CFG, $DB;
-        $this->available_types = array('user' => get_string('user'), 'group' => get_string('group'));
+        $this->availabletypes = array('user' => get_string('user'), 'group' => get_string('group'));
 
         if (empty($id)) {
             $this->id = $USER->id;
@@ -1042,18 +1077,18 @@
             $this->overrides = array('course', 'site');
 
             $this->tables['gm'] = 'groups_members';
-            $this->conditions[] = 'b.userid = gm.userid';
+            $this->conditions[] = 'p.userid = gm.userid';
             $this->conditions[] = 'gm.groupid = ?';
             $this->params[]     = $this->id;
 
             if (!empty($CFG->useblogassociations)) {  // only show blog entries associated with this course
-                $course_context     = get_context_instance(CONTEXT_COURSE, $DB->get_field('groups', 'courseid', array('id' => $this->id)));
+                $coursecontext     = get_context_instance(CONTEXT_COURSE, $DB->get_field('groups', 'courseid', array('id' => $this->id)));
                 $this->tables['ba'] = 'blog_association';
                 $this->conditions[] = 'gm.groupid = ?';
                 $this->conditions[] = 'ba.contextid = ?';
-                $this->conditions[] = 'ba.blogid = b.id';
+                $this->conditions[] = 'ba.blogid = p.id';
                 $this->params[]     = $this->id;
-                $this->params[]     = $course_context->id;
+                $this->params[]     = $coursecontext->id;
             }
         }
 
@@ -1064,7 +1099,7 @@
  * This filter defines a tag by which blog entries should be searched.
  */
 class blog_filter_tag extends blog_filter {
-    public $tables = array('t' => 'tag', 'ti' => 'tag_instance', 'b' => 'blog_entries');
+    public $tables = array('t' => 'tag', 'ti' => 'tag_instance', 'p' => 'post');
 
     /**
      * Constructor
@@ -1076,8 +1111,8 @@
         $this->id = $id;
 
         $this->conditions = array('ti.tagid = t.id',
-                                  "ti.itemtype = 'blog_entries'",
-                                  'ti.itemid = b.id',
+                                  "ti.itemtype = 'post'",
+                                  'ti.itemid = p.id',
                                   't.id = ?');
         $this->params = array($this->id);
     }
@@ -1097,15 +1132,29 @@
 }
 
 /**
+ * This filter restricts the results to a time interval in seconds up to mktime()
+ */
+class blog_filter_since extends blog_filter {
+    public function __construct($interval) {
+        $this->conditions[] = 'p.lastmodified >= ? AND p.lastmodified <= ?';
+        $this->params[] = mktime() - $interval;
+        $this->params[] = mktime();
+    }
+}
+
+/**
  * Filter used to perform full-text search on an entry's subject, summary and content
  */
 class blog_filter_search extends blog_filter {
 
-    public function __construct($search_term) {
+    public function __construct($searchterm) {
         global $DB;
         $ilike = $DB->sql_ilike();
-        $this->conditions = array("(b.summary $ilike '%$search_term%' OR
-                                    b.content $ilike '%$search_term%' OR
-                                    b.subject $ilike '%$search_term%')");
+        $this->conditions = array("(p.summary $ilike ? OR
+                                    p.content $ilike ? OR
+                                    p.subject $ilike ?)");
+        $this->params[] = "%$searchterm%";
+        $this->params[] = "%$searchterm%";
+        $this->params[] = "%$searchterm%";
     }
 }
Index: blog/rsslib.php
=========================================================
--- blog/rsslib.php	(revision 1.20)
+++ blog/rsslib.php	Fri Oct 23 14:14:18 WST 2009
@@ -1,166 +1,165 @@
 <?php
 
-    require_once($CFG->dirroot.'/lib/rsslib.php');
-    require_once($CFG->dirroot .'/blog/lib.php');
-
+require_once($CFG->dirroot.'/lib/rsslib.php');
+require_once($CFG->dirroot .'/blog/lib.php');
 
-    // This function returns the icon (from theme) with the link to rss/file.php
-    // needs some hacking to rss/file.php
-    function blog_rss_print_link($filtertype, $filterselect, $tagid=0, $tooltiptext='') {
+// This function returns the icon (from theme) with the link to rss/file.php
+// needs some hacking to rss/file.php
+function blog_rss_print_link($filtertype, $filterselect, $tagid=0, $tooltiptext='') {
 
-        global $CFG, $USER, $OUTPUT;
+    global $CFG, $USER, $OUTPUT;
 
-        if (empty($USER->id)) {
-            $userid = 1;
-        } else {
-            $userid = $USER->id;
-        }
+    if (empty($USER->id)) {
+        $userid = 1;
+    } else {
+        $userid = $USER->id;
+    }
 
-        switch ($filtertype) {
-            case 'site':
-                $path = SITEID.'/'.$userid.'/blog/site/'.SITEID;
-            break;
-            case 'course':
-                $path = $filterselect.'/'.$userid.'/blog/course/'.$filterselect;
-            break;
-            case 'group':
-                $path = SITEID.'/'.$userid.'/blog/group/'.$filterselect;
-            break;
-            case 'user':
-                $path = SITEID.'/'.$userid.'/blog/user/'.$filterselect;
-            break;
-        }
+    switch ($filtertype) {
+        case 'site':
+            $path = SITEID.'/'.$userid.'/blog/site/'.SITEID;
+        break;
+        case 'course':
+            $path = $filterselect.'/'.$userid.'/blog/course/'.$filterselect;
+        break;
+        case 'group':
+            $path = SITEID.'/'.$userid.'/blog/group/'.$filterselect;
+        break;
+        case 'user':
+            $path = SITEID.'/'.$userid.'/blog/user/'.$filterselect;
+        break;
+    }
 
-        if ($tagid) {
-            $path .= '/'.$tagid;
-        }
+    if ($tagid) {
+        $path .= '/'.$tagid;
+    }
 
-        $path .= '/rss.xml';
-        $rsspix = $OUTPUT->old_icon_url('i/rss');
+    $path .= '/rss.xml';
+    $rsspix = $OUTPUT->old_icon_url('i/rss');
 
-        require_once($CFG->libdir.'/filelib.php');
-        $path = get_file_url($path, null, 'rssfile');
-        print '<div class="mdl-right"><a href="'. $path .'"><img src="'. $rsspix .'" title="'. strip_tags($tooltiptext) .'" alt="'.get_string('rss').'" /></a></div>';
+    require_once($CFG->libdir.'/filelib.php');
+    $path = get_file_url($path, null, 'rssfile');
+    print '<div class="mdl-right"><a href="'. $path .'"><img src="'. $rsspix .'" title="'. strip_tags($tooltiptext) .'" alt="'.get_string('rss').'" /></a></div>';
 
-    }
+}
 
 
-    // Generate any blog RSS feed via one function (called by ../rss/file.php)
-    function blog_generate_rss_feed($type, $id, $tagid=0) {
+// Generate any blog RSS feed via one function (called by ../rss/file.php)
+function blog_generate_rss_feed($type, $id, $tagid=0) {
-        global $CFG, $SITE, $DB;
+    global $CFG, $SITE, $DB;
 
-        if (empty($CFG->enablerssfeeds)) {
-            debugging('Sorry, RSS feeds are disabled on this site');
-            return '';
-        }
+    if (empty($CFG->enablerssfeeds)) {
+        debugging('Sorry, RSS feeds are disabled on this site');
+        return '';
+    }
 
-        $filename = blog_rss_file_name($type, $id, $tagid);
+    $filename = blog_rss_file_name($type, $id, $tagid);
 
-        if (file_exists($filename)) {
-            if (filemtime($filename) + 3600 > time()) {
-                return $filename;   /// It's already done so we return cached version
-            }
-        }
+    if (file_exists($filename)) {
+        if (filemtime($filename) + 3600 > time()) {
+            return $filename;   /// It's already done so we return cached version
+        }
+    }
 
-    /// Get all the entries from the database
+/// Get all the entries from the database
 
-        $blogentries = blog_fetch_entries('', 20, '', $type, $id, $tagid);
+    $blogentries = blog_fetch_entries('', 20, '', $type, $id, $tagid);
 
-    /// Now generate an array of RSS items
+/// Now generate an array of RSS items
-        if ($blogentries) {
-            $items = array();
-            foreach ($blogentries as $blog_entry) {
-                $item = NULL;
-                $item->author = fullname($DB->get_record('user', array('id'=>$blog_entry->userid))); // TODO: this is slow
-                $item->title = $blog_entry->subject;
-                $item->pubdate = $blog_entry->lastmodified;
-                $item->link = $CFG->wwwroot.'/blog/index.php?entryid='.$blog_entry->id;
-                $item->description = format_text($blog_entry->summary, $blog_entry->format);
+    if ($blogentries) {
+        $items = array();
+        foreach ($blogentries as $blog_entry) {
+            $item = NULL;
+            $item->author = fullname($DB->get_record('user', array('id'=>$blog_entry->userid))); // TODO: this is slow
+            $item->title = $blog_entry->subject;
+            $item->pubdate = $blog_entry->lastmodified;
+            $item->link = $CFG->wwwroot.'/blog/index.php?entryid='.$blog_entry->id;
+            $item->description = format_text($blog_entry->summary, $blog_entry->format);
-                if ( !empty($CFG->usetags) && ($blogtags = tag_get_tags_array('blog_entries', $blog_entry->id)) ) {
+            if ( !empty($CFG->usetags) && ($blogtags = tag_get_tags_array('post', $blog_entry->id)) ) {
-                    if ($blogtags) {
-                        $item->tags = $blogtags;
-                    }
-                    $item->tagscheme = $CFG->wwwroot . '/tag';
-                }
-                $items[] = $item;
-            }
-            $articles = rss_add_items($items);   /// Change structure to XML
-        } else {
-            $articles = '';
-        }
+                if ($blogtags) {
+                    $item->tags = $blogtags;
+                }
+                $item->tagscheme = $CFG->wwwroot . '/tag';
+            }
+            $items[] = $item;
+        }
+        $articles = rss_add_items($items);   /// Change structure to XML
+    } else {
+        $articles = '';
+    }
 
-    /// Get header and footer information
+/// Get header and footer information
 
-        switch ($type) {
-            case 'user':
-                $info = fullname($DB->get_record('user', array('id'=>$id), 'firstname,lastname'));
-                break;
-            case 'course':
-                $info = $DB->get_field('course', 'fullname', array('id'=>$id));
-                break;
-            case 'site':
-                $info = $SITE->fullname;
-                break;
-            case 'group':
-                $group = groups_get_group($id, false);
-                $info = $group->name; //TODO: $DB->get_field('groups', 'name', array('id'=>$id))
-                break;
-            default:
-                $info = '';
-                break;
-        }
+    switch ($type) {
+        case 'user':
+            $info = fullname($DB->get_record('user', array('id'=>$id), 'firstname,lastname'));
+            break;
+        case 'course':
+            $info = $DB->get_field('course', 'fullname', array('id'=>$id));
+            break;
+        case 'site':
+            $info = $SITE->fullname;
+            break;
+        case 'group':
+            $group = groups_get_group($id, false);
+            $info = $group->name; //TODO: $DB->get_field('groups', 'name', array('id'=>$id))
+            break;
+        default:
+            $info = '';
+            break;
+    }
 
-        if ($tagid) {
-            $info .= ': '.$DB->get_field('tags', 'text', array('id'=>$tagid));
-        }
+    if ($tagid) {
+        $info .= ': '.$DB->get_field('tags', 'text', array('id'=>$tagid));
+    }
 
-        $header = rss_standard_header(get_string($type.'blog','blog', $info),
-                                      $CFG->wwwroot.'/blog/index.php',
-                                      get_string('intro','blog'));
+    $header = rss_standard_header(get_string($type.'blog','blog', $info),
+                                  $CFG->wwwroot.'/blog/index.php',
+                                  get_string('intro','blog'));
 
-        $footer = rss_standard_footer();
+    $footer = rss_standard_footer();
 
 
-    /// Save the XML contents to file.
+/// Save the XML contents to file.
 
-        $rssdata = $header.$articles.$footer;
+    $rssdata = $header.$articles.$footer;
 
-        if (blog_rss_save_file($type,$id,$tagid,$rssdata)) {
-            return $filename;
-        } else {
-            return false;   // Couldn't find it or make it
-        }
+    if (blog_rss_save_file($type,$id,$tagid,$rssdata)) {
+        return $filename;
+    } else {
+        return false;   // Couldn't find it or make it
+    }
-    }
+}
 
 
-    function blog_rss_file_name($type, $id, $tagid=0) {
+function blog_rss_file_name($type, $id, $tagid=0) {
-        global $CFG;
+    global $CFG;
 
-        if ($tagid) {
-            return "$CFG->dataroot/rss/blog/$type/$id/$tagid.xml";
-        } else {
-            return "$CFG->dataroot/rss/blog/$type/$id.xml";
-        }
+    if ($tagid) {
+        return "$CFG->dataroot/rss/blog/$type/$id/$tagid.xml";
+    } else {
+        return "$CFG->dataroot/rss/blog/$type/$id.xml";
+    }
-    }
+}
 
-    //This function saves to file the rss feed specified in the parameters
-    function blog_rss_save_file($type, $id, $tagid=0, $contents='') {
+//This function saves to file the rss feed specified in the parameters
+function blog_rss_save_file($type, $id, $tagid=0, $contents='') {
-        global $CFG;
+    global $CFG;
 
-        if (! $basedir = make_upload_directory("rss/blog/$type/$id")) {
-            return false;
-        }
+    if (! $basedir = make_upload_directory("rss/blog/$type/$id")) {
+        return false;
+    }
 
-        $file = blog_rss_file_name($type, $id, $tagid);
-        $rss_file = fopen($file, 'w');
-        if ($rss_file) {
-            $status = fwrite ($rss_file, $contents);
-            fclose($rss_file);
-        } else {
-            $status = false;
-        }
+    $file = blog_rss_file_name($type, $id, $tagid);
+    $rss_file = fopen($file, 'w');
+    if ($rss_file) {
+        $status = fwrite ($rss_file, $contents);
+        fclose($rss_file);
+    } else {
+        $status = false;
+    }
 
-        return $status;
+    return $status;
-    }
+}
 
 ?>
Index: blog/simpletest/testbloglib.php
=========================================================
--- blog/simpletest/testbloglib.php	(revision 1.2)
+++ blog/simpletest/testbloglib.php	Tue Oct 27 15:06:43 WST 2009
@@ -32,6 +32,7 @@
     public static $includecoverage = array('blog/locallib.php');
 
     public function test_overrides() {
+
         // Try all the filters at once: Only the entry filter is active
         $blog_listing = new blog_listing(array('site' => 1, 'course' => 1, 'module' => 1, 'group' => 1, 'user' => 1, 'tag' => 1, 'entry' => 1));
         $this->assertFalse(array_key_exists('site', $blog_listing->filters));
@@ -60,28 +61,6 @@
         $this->assertTrue(array_key_exists('user', $blog_listing->filters));
         $this->assertTrue(array_key_exists('tag', $blog_listing->filters));
 
-        // Now use the group and module together
-        $blog_listing = new blog_listing(array('module' => 1, 'group' => 1, 'tag' => 1));
-        $this->assertTrue(array_key_exists('group', $blog_listing->filters));
-        $this->assertTrue(array_key_exists('module', $blog_listing->filters));
-        $this->assertFalse(array_key_exists('user', $blog_listing->filters));
-        $this->assertTrue(array_key_exists('tag', $blog_listing->filters));
-
-        $blog_listing = new blog_listing(array('course' => 2));
-        $this->assertTrue(array_key_exists('course', $blog_listing->filters));
-
-        $blog_listing = new blog_listing(array('course' => 2, 'group' => 12));
-        $this->assertFalse(array_key_exists('course', $blog_listing->filters));
-        $this->assertTrue(array_key_exists('group', $blog_listing->filters));
-
-        $blog_listing = new blog_listing(array('site' => 2, 'group' => 12));
-        $this->assertFalse(array_key_exists('site', $blog_listing->filters));
-        $this->assertTrue(array_key_exists('group', $blog_listing->filters));
-
-        $blog_listing = new blog_listing(array('user' => 2, 'group' => 12));
-        $this->assertFalse(array_key_exists('group', $blog_listing->filters));
-        $this->assertTrue(array_key_exists('user', $blog_listing->filters));
-
     }
 
     /**
@@ -89,38 +68,6 @@
      */
     public function test_blog_get_headers_case_1() {
         global $CFG, $PAGE, $OUTPUT;
-        
-        $this->create_test_tables('post', 'tag', 'course', 'user', 'role', 'role_assignments', 'group', 'blog_associations', 
-                                  'course_modules', 'role_capabilities', 'tag_correlation', 'tag_instance');
-        
-        $contexts = $this->load_test_data('context',
-                array('contextlevel', 'instanceid', 'path', 'depth'), array(
-           1 => array(40, 666, '', 2),
-           2 => array(50, 666, '', 3),
-           3 => array(70, 666, '', 4),
-        ));
-
-        $this->load_test_data('course', 
-                              array('id', 'fullname', 'shortname', 'format'), 
-                              array(
-                                array(1, 'My Moodle Site', 'moodle', 'site'),
-                                array(2, 'Course 1', 'course1', 'weeks'),
-                                array(3, 'Course 2', 'course2', 'weeks')
-                                )
-                              );
-        $this->load_test_data('user',
-                              array('id', 'confirmed', 'username', 'firstname', 'lastname'),
-                              array( array(1, 1, 'joebloe', 'Joe', 'Bloe')));
-
-        $this->switch_to_test_db();
-        
-        $userrole = create_role(get_string('authenticateduser'), 'user', get_string('authenticateduserdescription'), 'moodle/legacy:user');
-        $student = $this->testdb->get_record('role', array('shortname' => 'student'));
-        
-        $ras = $this->load_test_data('role_assignments', array('userid', 'roleid', 'contextid'),
-                                     array(array(1, $student->id, $context[2]->id)));
-
-        // Case 1: A single blog entry
         $PAGE->url = new moodle_url($CFG->wwwroot . '/blog/index.php', array('entryid' => 1));
         $blog_headers = blog_get_headers();
 
Index: course/report/outline/index.php
=========================================================
--- course/report/outline/index.php	(revision 1.26)
+++ course/report/outline/index.php	Thu Oct 22 14:45:11 WST 2009
@@ -31,6 +31,7 @@
     $strlast           = get_string('lastaccess');
     $strreports        = get_string('reports');
     $strviews          = get_string('views');
+    $strrelatedblogentries = get_string('relatedblogentries', 'blog');
 
     $PAGE->set_title($course->shortname .': '. $stractivityreport);
     $PAGE->set_heading($course->fullname);
@@ -43,15 +44,21 @@
         print_error('logfilenotavailable');
     }
 
-    echo '<div class="loginfo">'.get_string('computedfromlogs', 'admin', userdate($logstart)).'</div>';
+    echo $OUTPUT->container(get_string('computedfromlogs', 'admin', userdate($logstart)), 'loginfo');
+
+    $outlinetable = new html_table();
+    $outlinetable->add_class('generaltable boxaligncenter');
+    $outlinetable->cellpadding = 5;
+    $outlinetable->id = 'outlinetable';
+    $outlinetable->head = array($stractivity, $strviews);
 
-    echo '<table id="outlinetable" class="generaltable boxaligncenter" cellpadding="5"><tr>';
-    echo '<th class="header c0" scope="col">'.$stractivity.'</th>';
-    echo '<th class="header c1" scope="col">'.$strviews.'</th>';
+    if ($CFG->useblogassociations) {
+        $outlinetable->head[] = $strrelatedblogentries;
+    }
+
     if ($showlastaccess) {
-        echo '<th class="header c2" scope="col">'.$strlast.'</th>';
+        $outlinetable->head[] = $strlast;
     }
-    echo '</tr>';
 
     $modinfo = get_fast_modinfo($course);
 
@@ -63,7 +70,6 @@
           GROUP BY cm.id";
     $views = $DB->get_records_sql($sql, array($course->id));
 
-    $ri = 0;
     $prevsecctionnum = 0;
     foreach ($modinfo->sections as $sectionnum=>$section) {
         foreach ($section as $cmid) {
@@ -75,41 +81,81 @@
                 continue;
             }
             if ($prevsecctionnum != $sectionnum) {
-                echo '<tr class="r'.$ri++.' section"><td colspan="3"><h3>';
+                $sectionrow = new html_table_row();
+                $sectionrow->add_class('section');
+                $sectioncell = new html_table_cell();
+                $sectioncell->colspan = count($outlinetable->head);
+
+                $sectiontitle = '';
                 switch ($course->format) {
-                    case 'weeks': print_string('week'); break;
-                    case 'topics': print_string('topic'); break;
-                    default: print_string('section'); break;
+                    case 'weeks': $sectiontitle = get_string('week'); break;
+                    case 'topics': $sectiontitle = get_string('topic'); break;
+                    default: $sectiontitle = get_string('section'); break;
                 }
-                echo ' '.$sectionnum.'</h3></td></tr>';
+
+                $sectioncell->text = $OUTPUT->heading($sectiontitle . ' ' . $sectionnum, 3);
+                $sectionrow->cells[] = $sectioncell;
+                $outlinetable->data[] = $sectionrow;
 
                 $prevsecctionnum = $sectionnum;
             }
 
             $dimmed = $cm->visible ? '' : 'class="dimmed"';
             $modulename = get_string('modulename', $cm->modname);
-            echo '<tr class="r'.$ri++.'">';
-            echo "<td class=\"cell c0 actvity\"><img src=\"" . $OUTPUT->mod_icon_url('icon', $cm->modname) . "\" class=\"icon\" alt=\"$modulename\" />";
-            echo "<a $dimmed title=\"$modulename\" href=\"$CFG->wwwroot/mod/$cm->modname/view.php?id=$cm->id\">".format_string($cm->name)."</a></td>";
-            echo "<td class=\"cell c1 numviews\">";
+
+            $reportrow = new html_table_row();
+            $activitycell = new html_table_cell();
+            $activitycell->add_class('activity');
+
+            $activityicon = html_image::make($OUTPUT->mod_icon_url('icon', $cm->modname));
+            $activityicon->add_class('icon');
+            $activityicon->alt = $modulename;
+
+            $activitylink = html_link::make("$CFG->wwwroot/mod/$cm->modname/view.php?id=$cm->id", format_string($cm->name));
+            if (!$cm->visible) {
+                $activitylink->add_class('dimmed');
+            }
+
+            $activitycell->text = $OUTPUT->image($activityicon) . $OUTPUT->link($activitylink);
+
+            $reportrow->cells[] = $activitycell;
+
+            $numviewscell = new html_table_cell();
+            $numviewscell->add_class('numviews');
+
             if (!empty($views[$cm->id]->numviews)) {
-                echo $views[$cm->id]->numviews;
+                $numviewscell->text = $views[$cm->id]->numviews;
+            } else {
+                $numviewscell->text = '-';
+            }
+
+            $reportrow->cells[] = $numviewscell;
+
+            if ($CFG->useblogassociations) {
+                $blogcell = new html_table_cell();
+                $blogcell->add_class('blog');
+                if ($blogcount = blog_get_associated_count($course->id, $cm->id)) {
+                    $blogcell->text = $OUTPUT->link(html_link::make('/blog/index.php?modid='.$cm->id, $blogcount));
-            } else {
+                } else {
-                echo '-';
+                    $blogcell->text = '-';
-            }
+                }
-            echo "</td>";
+                $reportrow->cells[] = $blogcell;
+            }
+
             if ($showlastaccess) {
-                echo "<td class=\"cell c2 lastaccess\">";
+                $lastaccesscell = new html_table_cell();
+                $lastaccesscell->add_class('lastaccess');
+
                 if (isset($views[$cm->id]->lasttime)) {
                     $timeago = format_time(time() - $views[$cm->id]->lasttime);
-                    echo userdate($views[$cm->id]->lasttime)." ($timeago)";
+                    $lastaccesscell->text = userdate($views[$cm->id]->lasttime)." ($timeago)";
                 }
-                echo "</td>";
+                $reportrow->cells[] = $lastaccesscell;
             }
-            echo '</tr>';
+            $outlinetable->data[] = $reportrow;
         }
     }
-    echo '</table>';
+    echo $OUTPUT->table($outlinetable);
 
     echo $OUTPUT->footer();
 
Index: course/reset_form.php
=========================================================
--- course/reset_form.php	(revision 1.6)
+++ course/reset_form.php	Thu Oct 15 09:39:03 WST 2009
@@ -14,6 +14,8 @@
         $mform->addElement('checkbox', 'reset_events', get_string('deleteevents', 'calendar'));
         $mform->addElement('checkbox', 'reset_logs', get_string('deletelogs'));
         $mform->addElement('checkbox', 'reset_notes', get_string('deletenotes', 'notes'));
+        $mform->addElement('checkbox', 'delete_blog_associations', get_string('deleteblogassociations', 'blog'));
+        $mform->setHelpButton('delete_blog_associations', array('deleteblogassociations', get_string('deleteblogassociations', 'blog'), 'blog'));
 
 
         $mform->addElement('header', 'rolesheader', get_string('roles'));
Index: lang/en_utf8/admin.php
=========================================================
--- lang/en_utf8/admin.php	(revision 1.306)
+++ lang/en_utf8/admin.php	Fri Oct 16 15:13:12 WST 2009
@@ -38,6 +38,33 @@
 $string['blockmultiple'] = 'Multiple';
 $string['blocksettings'] = 'Manage blocks';
 $string['bloglevel'] = 'Blog visibility';
+$string['bloglevelupgrade'] = 'Blog visibility upgrade';
+$string['bloglevelupgradebody'] = 'Hi! You are receiving this message as an administrator of $a->sitename.
+
+This site has recently been upgraded to Moodle 2.0.
+
+Blog visibility was simplified in 2.0, but your site still uses one of the old visibility types.
+
+To preserve the course-based or group-based visibility of the blog entries on your site, you need to run the following upgrade script, which will create a special "blog" type forum in each course whose enrolled users have posted blog entries, and will copy these blog entries in this special forum.
+
+Blogs will then be entirely switched off at the site level. No blog entries will be deleted in the process.
+
+You can run the script by visiting $a->fixurl.
+';
+$string['bloglevelupgradehtml'] = '<p>Hi! You are receiving this message as an administrator of $a->sitename.</p>
+
+<p>This site has recently been upgraded to Moodle 2.0.</p>
+
+<p>Blog visibility was simplified in 2.0, but your site still uses one of the old visibility types. </p>
+
+<p>To preserve the course-based or group-based visibility of the blog entries on your site, you need to run the following upgrade script, which will create a special "blog" type forum in each course whose enrolled users have posted blog entries, and will copy these blog entries in this special forum. </p>
+
+<p>Blogs will then be entirely switched off at the site level. No blog entries will be deleted in the process.</p>
+
+<p>You can run the script by visiting <a href=\"$a->fixurl\">the blog level upgrade page</a>.</p>';
+$string['bloglevelupgradeinfo'] = 'Blog visibility was simplified in 2.0, but your site still uses one of the old visibility types. To preserve the course-based or group-based visibility of the blog entries on your site, the following upgrade script will create a special "blog" type forum in each course whose enrolled users have posted blog entries, and will copy these blog entries in this special forum. Blogs will then be entirely switched off at the site level. No blog entries will be deleted in the process.';
+$string['bloglevelupgradenotice'] = 'Your site is using an old blog visibility setting, the <a href=\"bloglevelupgrade.php\">blog visibility upgrade</a> is recommended.';
+$string['bloglevelupgradeprogress'] = 'Conversion progress: $a->userscount users reviewed, $a->blogcount entries converted.';
 $string['bookmarkadded'] = 'Bookmark added.';
 $string['bookmarkalreadyexists'] = 'You have already bookmarked this page.';
 $string['bookmarkdeleted'] = 'Bookmark deleted.';
Index: lang/en_utf8/block_blog_recent.php
=========================================================
--- lang/en_utf8/block_blog_recent.php	(revision 0)
+++ lang/en_utf8/block_blog_recent.php	Thu Oct 15 11:09:32 WST 2009
@@ -0,0 +1,5 @@
+<?php
+$string['blockname'] = 'Recent blog entries';
+$string['norecentblogentries'] = 'No recent entries';
+$string['numentriestodisplay'] = 'Number of recent entries to display';
+$string['recentinterval'] = 'Interval of time considered \"recent\"';
Index: lang/en_utf8/blog.php
=========================================================
--- lang/en_utf8/blog.php	(revision 1.31)
+++ lang/en_utf8/blog.php	Wed Oct 28 15:59:14 WST 2009
@@ -3,10 +3,15 @@
 
 
 $string['addnewentry'] = 'Add a new entry';
-$string['addnewexternalblog'] = 'New external blog...';
+$string['addnewexternalblog'] = 'Register an external blog...';
 $string['assocdescription'] = 'If you are writing about a course and/or activity modules, select them here.';
+$string['associatewithcourse'] = 'Blog about course $a->coursename';
+$string['associatewithmodule'] = 'Blog about $a->modtype: $a->modname';
+$string['associated'] = 'Associated $a';
+$string['association'] = 'Association';
 $string['associations'] = 'Associations';
 $string['associationunviewable'] = 'This entry cannot be viewed by others until a course is associated with it or the \'Publish To\' field is changed';
+$string['autotags'] = 'Tags associated with each imported blog entry';
 $string['backupblogshelp'] = 'If enabled then blogs will be included in SITE automated backups';
 $string['blockexternalstitle'] = 'External Blogs';
 $string['blockmenutitle'] = 'Blog Menu';
@@ -14,6 +19,7 @@
 $string['blocktagstitle'] = 'Blog Tags';
 $string['blocktitle'] = 'Blog tags block title';
 $string['blog'] = 'Blog';
+$string['blogaboutthis'] = 'Blog about this $a->type';
 $string['blogadministration'] = 'Blog administration';
 $string['blogdeleteconfirm'] = 'Delete this blog?';
 $string['blogdisable'] = 'Blogging is disabled!';
@@ -32,20 +38,21 @@
 $string['cannotviewsiteblog'] = 'You do not have the required permissions to view all site blogs';
 $string['cannotviewuserblog'] = 'You do not have the required permissions to read user blogs';
 $string['configexternalblogcrontime'] = 'How often Moodle checks the external blogs for new entries.';
-$string['configuseexternalblogs'] = 'Enables users to add links to external blogs. Moodle regularly checks if these blogs, then copies new entries to the blog of the Moodle user.';
 $string['configmaxexternalblogsperuser'] = 'The number of external blogs each user is allowed to link to their Moodle blog.';
 $string['configuseblogassociations'] = 'Enables the association of blog entries with courses and course modules.';
+$string['configuseexternalblogs'] = 'Enables users to add links to external blogs. Moodle regularly checks if these blogs, then copies new entries to the blog of the Moodle user.';
 $string['courseblog'] = 'Course blog: $a';
 $string['courseblogdisable'] = 'Course blogs is not enabled';
 $string['courseblogs'] = 'Users can only see blogs for people who share a course';
+$string['deleteblogassociations'] = 'Delete blog associations';
 $string['deleteotagswarn'] = 'Are you sure you want to remove this / these tags <br />from all blog posts and remove it from the system?';
 $string['disableblogs'] = 'Disable blog system completely';
 $string['donothaveblog'] = 'You do not have your own blog, sorry.';
 $string['editentry'] = 'Edit a blog entry';
-$string['editexternalblog'] = 'Edit an external blog';
+$string['editexternalblog'] = 'Edit this external blog';
 $string['emptybody'] = 'Blog entry body can not be empty';
+$string['emptyrssfeed'] = 'The URL you entered does not point to a valid RSS feed';
 $string['emptytitle'] = 'Blog entry title can not be empty';
-$string['emptyrssfeed'] = 'The URL you entered does not point to a valid RSS feed';
 $string['entrybody'] = 'Blog entry body';
 $string['entrybodyonlydesc'] = 'Entry description';
 $string['entryerrornotyours'] = 'This entry is not yours';
@@ -54,7 +61,11 @@
 $string['entryupdated'] = 'Blog entry updated';
 $string['externalblogcrontime'] = 'External blog cron schedule';
 $string['externalblogs'] = 'External blogs';
+$string['externalblogdeleted'] = 'External blog deleted';
+$string['feedisinvalid'] = 'This feed is invalid';
+$string['feedisvalid'] = 'This feed is valid';
 $string['filterblogsby'] = 'Filter entries by...';
+$string['filtertags'] = 'External tags';
 $string['groupblog'] = 'Group blog: $a';
 $string['groupblogdisable'] = 'Group blog is not enabled';
 $string['groupblogentries'] = 'Blog entries associated with $a->coursename by group $a->groupname';
@@ -65,9 +76,11 @@
 $string['invalidurl'] = 'This URL is unreachable';
 $string['linktooriginalentry'] = 'Link to original blog entry';
 $string['maxexternalblogsperuser'] = 'Maximum number of external blogs per user';
+$string['modulename'] = 'Blog';
 $string['mustassociatecourse'] = 'If you are publishing to course or group members, you must associate a course with this entry';
 $string['noentriesyet'] = 'No visible entries here';
 $string['noguestpost'] = 'Guest can not post blogs!';
+$string['nopermissionstodeleteentry'] = 'You lack the permissions required to delete this blog entry';
 $string['norighttodeletetag'] = 'You have no rights to delete this tag - $a';
 $string['nosuchentry'] = 'No such blog entry';
 $string['notallowedtoedit'] = 'You are not allowed to edit this entry';
@@ -88,8 +101,10 @@
 $string['publishtosite'] = 'Anyone on this site';
 $string['publishtoworld'] = 'Anyone in the world';
 $string['readfirst'] = 'Read this first';
+$string['relatedblogentries'] = 'Related blog entries';
+$string['retrievedfrom'] = 'Retrieved from ';
+$string['searchterm'] = 'Search: $a';
 $string['settingsupdatederror'] = 'An error has occurred, blog preference setting could not be updated';
-$string['searchterm'] = 'Search: $a';
 $string['siteblog'] = 'Site blog: $a';
 $string['siteblogdisable'] = 'Site blog is not enabled';
 $string['siteblogs'] = 'All site users can see all blog entries';
@@ -98,14 +113,17 @@
 $string['tags'] = 'Tags';
 $string['tagsort'] = 'Sort the tag display by';
 $string['tagtext'] = 'Tag text';
+$string['timefetched'] = 'Time of last sync';
 $string['timewithin'] = 'Display tags used within this many days';
 $string['updateentrywithid'] = 'Updating entry';
-$string['userblog'] = 'User blog: $a';
 $string['useblogassociations'] = 'Enable blog associations';
 $string['useexternalblogs'] = 'Enable external blogs';
+$string['userblog'] = 'User blog: $a';
 $string['userblogentries'] = 'Blog entries by $a';
+$string['valid'] = 'Valid';
 $string['viewallblogentries'] = 'All entries about this $a';
-$string['viewblogentries'] = 'Entries about this $a';
+$string['viewallmodentries'] = 'All entries about this $a->type';
+$string['viewblogentries'] = 'Entries about this $a->type';
 $string['viewblogsfor'] = 'View all entries for...';
 $string['viewcourseblogs'] = 'View entries for course...';
 $string['viewgroupblogs'] = 'View entries for group...';
Index: lang/en_utf8/forum.php
=========================================================
--- lang/en_utf8/forum.php	(revision 1.61)
+++ lang/en_utf8/forum.php	Thu Oct 15 09:39:03 WST 2009
@@ -30,6 +30,7 @@
 $string['blockafter'] = 'Post threshold for blocking';
 $string['blockperiod'] = 'Time period for blocking';
 $string['blockperioddisabled'] = 'Don\'t block';
+$string['blogforum'] = 'Standard forum displayed in a blog-like format';
 $string['bynameondate'] = 'by $a->name - $a->date';
 $string['cannotadd'] = 'Could not add the discussion for this forum';
 $string['cannotaddteacherforumto'] = 'Could not add converted teacher forum instance to section 0 in the course';
@@ -164,6 +165,7 @@
 $string['generalforum'] = 'Standard forum for general use';
 $string['generalforums'] = 'General forums';
 $string['inforum'] = 'in $a';
+$string['introblog'] = 'The posts in this forum were copied here automatically from blogs of users in this course because those blog entries are no longer available';
 $string['intronews'] = 'General news and announcements';
 $string['introsocial'] = 'An open forum for chatting about anything you want to';
 $string['introteacher'] = 'A forum for teacher-only notes and discussion';
Index: lang/en_utf8/help/blog/deleteblogassociations.html
=========================================================
--- lang/en_utf8/help/blog/deleteblogassociations.html	(revision 0)
+++ lang/en_utf8/help/blog/deleteblogassociations.html	Wed Oct 21 10:31:52 WST 2009
@@ -0,0 +1,2 @@
+<h1>Delete blog associations</h1>
+<p>The blog entries will not be deleted, but they will no longer be associated with this course or its activities and resources.</p>
Index: lang/en_utf8/help/blog/filtertags.html
=========================================================
--- lang/en_utf8/help/blog/filtertags.html	(revision 0)
+++ lang/en_utf8/help/blog/filtertags.html	Tue Oct 20 15:21:49 WST 2009
@@ -0,0 +1,3 @@
+<h1>External tags</h1>
+
+<p>A comma-separated list of Tags defined in the external blog, and used by the synchronisation process to filter which entries are copied into the Moodle blog.</p>
Index: lib/db/access.php
=========================================================
--- lib/db/access.php	(revision 1.108)
+++ lib/db/access.php	Thu Oct 15 09:39:03 WST 2009
@@ -798,7 +798,22 @@
     'moodle/blog:view' => array(
 
         'captype' => 'read',
-        'contextlevel' => CONTEXT_COURSE,
+        'contextlevel' => CONTEXT_SYSTEM,
+        'legacy' => array(
+            'guest' => CAP_ALLOW,
+            'user' => CAP_ALLOW,
+            'student' => CAP_ALLOW,
+            'teacher' => CAP_ALLOW,
+            'editingteacher' => CAP_ALLOW,
+            'admin' => CAP_ALLOW
+        )
+    ),
+
+    'moodle/blog:search' => array(
+
+        'riskbitmask' => RISK_PERSONAL,
+        'captype' => 'read',
+        'contextlevel' => CONTEXT_SYSTEM,
         'legacy' => array(
             'guest' => CAP_ALLOW,
             'user' => CAP_ALLOW,
@@ -809,6 +824,16 @@
         )
     ),
 
+    'moodle/blog:viewdrafts' => array(
+
+        'riskbitmask' => RISK_PERSONAL,
+        'captype' => 'read',
+        'contextlevel' => CONTEXT_SYSTEM,
+        'legacy' => array(
+            'admin' => CAP_ALLOW
+        )
+    ),
+
     'moodle/blog:create' => array( // works in CONTEXT_SYSTEM only
 
         'riskbitmask' => RISK_SPAM,
@@ -826,14 +851,14 @@
         'riskbitmask' => RISK_SPAM,
 
         'captype' => 'write',
-        'contextlevel' => CONTEXT_COURSE,
+        'contextlevel' => CONTEXT_SYSTEM,
         'legacy' => array(
             'teacher' => CAP_ALLOW,
             'editingteacher' => CAP_ALLOW,
             'admin' => CAP_ALLOW
         )
     ),
-    
+
     'moodle/blog:manageexternal' => array(
 
         'riskbitmask' => RISK_SPAM,
@@ -849,6 +874,31 @@
         )
     ),
 
+    'moodle/blog:associatecourse' => array(
+
+        'captype' => 'write',
+        'contextlevel' => CONTEXT_COURSE,
+        'legacy' => array(
+            'student' => CAP_ALLOW,
+            'user' => CAP_ALLOW,
+            'teacher' => CAP_ALLOW,
+            'editingteacher' => CAP_ALLOW,
+            'admin' => CAP_ALLOW
+        )
+    ),
+
+    'moodle/blog:associatemodule' => array(
+
+        'captype' => 'write',
+        'contextlevel' => CONTEXT_MODULE,
+        'legacy' => array(
+            'student' => CAP_ALLOW,
+            'user' => CAP_ALLOW,
+            'teacher' => CAP_ALLOW,
+            'editingteacher' => CAP_ALLOW,
+            'admin' => CAP_ALLOW
+        )
+    ),
 
     'moodle/calendar:manageownentries' => array( // works in CONTEXT_SYSTEM only
 
Index: lib/db/install.xml
=========================================================
--- lib/db/install.xml	(revision 1.218)
+++ lib/db/install.xml	Tue Oct 27 13:41:17 WST 2009
@@ -2291,7 +2291,7 @@
         <KEY NAME="userid" TYPE="foreign" FIELDS="userid" REFTABLE="user" REFFIELDS="id" PREVIOUS="externalserviceid"/>
       </KEYS>
     </TABLE>
-    <TABLE NAME="external_tokens" COMMENT="Security tokens for accessing of external services" PREVIOUS="external_services_users">
+    <TABLE NAME="external_tokens" COMMENT="Security tokens for accessing of external services" PREVIOUS="external_services_users" NEXT="blog_association">
       <FIELDS>
         <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="token"/>
         <FIELD NAME="token" TYPE="char" LENGTH="128" NOTNULL="true" SEQUENCE="false" COMMENT="security token, aka private access key" PREVIOUS="id" NEXT="tokentype"/>
@@ -2312,5 +2312,34 @@
         <KEY NAME="contextid" TYPE="foreign" FIELDS="contextid" REFTABLE="context" REFFIELDS="id" PREVIOUS="externalserviceid"/>
       </KEYS>
     </TABLE>
+    <TABLE NAME="blog_association" COMMENT="Associations of blog entries with courses and module instances" PREVIOUS="external_tokens" NEXT="blog_external">
+      <FIELDS>
+        <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="contextid"/>
+        <FIELD NAME="contextid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" PREVIOUS="id" NEXT="blogid"/>
+        <FIELD NAME="blogid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" PREVIOUS="contextid"/>
+      </FIELDS>
+      <KEYS>
+        <KEY NAME="primary" TYPE="primary" FIELDS="id" NEXT="contextid"/>
+        <KEY NAME="contextid" TYPE="foreign" FIELDS="contextid" REFTABLE="context" REFFIELDS="id" PREVIOUS="primary" NEXT="blogid"/>
+        <KEY NAME="blogid" TYPE="foreign" FIELDS="blogid" REFTABLE="post" REFFIELDS="id" PREVIOUS="contextid"/>
+      </KEYS>
+    </TABLE>
+    <TABLE NAME="blog_external" COMMENT="External blog links used for RSS copying of blog entries to Moodle user blogs" PREVIOUS="blog_association">
+      <FIELDS>
+        <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="userid"/>
+        <FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" PREVIOUS="id" NEXT="name"/>
+        <FIELD NAME="name" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="userid" NEXT="description"/>
+        <FIELD NAME="description" TYPE="text" LENGTH="small" NOTNULL="false" SEQUENCE="false" PREVIOUS="name" NEXT="url"/>
+        <FIELD NAME="url" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" PREVIOUS="description" NEXT="filtertags"/>
+        <FIELD NAME="filtertags" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false" COMMENT="Comma-separated list of tags that will be used to filter which entries are copied over from the external blog. They refer to existing tags in the external blog." PREVIOUS="url" NEXT="failedlastsync"/>
+        <FIELD NAME="failedlastsync" TYPE="int" LENGTH="1" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="Whether or not the last sync failed for some reason" PREVIOUS="filtertags" NEXT="timemodified"/>
+        <FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="false" UNSIGNED="true" SEQUENCE="false" PREVIOUS="failedlastsync" NEXT="timefetched"/>
+        <FIELD NAME="timefetched" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="timemodified"/>
+      </FIELDS>
+      <KEYS>
+        <KEY NAME="primary" TYPE="primary" FIELDS="id" NEXT="userid"/>
+        <KEY NAME="userid" TYPE="foreign" FIELDS="userid" REFTABLE="user" REFFIELDS="id" PREVIOUS="primary"/>
+      </KEYS>
+    </TABLE>
   </TABLES>
 </XMLDB>
Index: lib/db/upgrade.php
=========================================================
--- lib/db/upgrade.php	(revision 1.339)
+++ lib/db/upgrade.php	Tue Oct 27 13:42:27 WST 2009
@@ -2629,6 +2629,83 @@
         upgrade_main_savepoint($result, 2009102600);
     }
 
+   if ($result && $oldversion < 2009102700) {
+
+    /// Define table blog_association to be created
+        $table = new xmldb_table('blog_association');
+
+    /// Adding fields to table blog_association
+        $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
+        $table->add_field('contextid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null);
+        $table->add_field('blogid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null);
+
+    /// Adding keys to table blog_association
+        $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
+        $table->add_key('contextid', XMLDB_KEY_FOREIGN, array('contextid'), 'context', array('id'));
+        $table->add_key('blogid', XMLDB_KEY_FOREIGN, array('blogid'), 'post', array('id'));
+
+    /// Conditionally launch create table for blog_association
+        if (!$dbman->table_exists($table)) {
+            $dbman->create_table($table);
+        }
+
+/// Define table blog_external to be created
+        $table = new xmldb_table('blog_external');
+
+    /// Adding fields to table blog_external
+        $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
+        $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null);
+        $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
+        $table->add_field('description', XMLDB_TYPE_TEXT, 'small', null, null, null, null);
+        $table->add_field('url', XMLDB_TYPE_TEXT, 'small', null, XMLDB_NOTNULL, null, null);
+        $table->add_field('filtertags', XMLDB_TYPE_CHAR, '255', null, null, null, null);
+        $table->add_field('failedlastsync', XMLDB_TYPE_INTEGER, '1', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
+        $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, null);
+        $table->add_field('timefetched', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
+
+    /// Adding keys to table blog_external
+        $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
+        $table->add_key('userid', XMLDB_KEY_FOREIGN, array('userid'), 'user', array('id'));
+
+    /// Conditionally launch create table for blog_external
+        if ($dbman->table_exists($table)) {
+            // Delete the existing one first (comes from early dev version)
+            $dbman->drop_table($table);
+        }
+        $dbman->create_table($table);
+
+        // Print notice about need to upgrade bloglevel
+        if (($CFG->bloglevel == BLOG_COURSE_LEVEL || $CFG->bloglevel == BLOG_GROUP_LEVEL) && empty($CFG->bloglevel_upgrade_complete)) {
+            echo $OUTPUT->notification(get_string('bloglevelupgradenotice', 'admin'));
+
+            // email admins about the need to upgrade their system using the admin/bloglevelupgrade.php script
+            $admins = get_admins();
+            $site = get_site();
+
+            $a = new StdClass;
+            $a->sitename = $site->fullname;
+            $a->fixurl   = "$CFG->wwwroot/$CFG->admin/bloglevelupgrade.php";
+
+            $subject   = get_string('bloglevelupgrade', 'admin');
+            $plainbody = get_string('bloglevelupgradebody', 'admin', $a);
+            $htmlbody  = get_string('bloglevelupgradehtml', 'admin', $a);
+
+            foreach ($admins as $admin) {
+                $eventdata = new object();
+                $eventdata->component = 'moodle';
+                $eventdata->userfrom = $USER;
+                $eventdata->userto = $admin;
+                $eventdata->subject = $subject;
+                $eventdata->fullmessage = $plainbody;
+                $eventdata->fullmessageformat = FORMAT_PLAIN;
+                $eventdata->fullmessagehtml = $htmlbody;
+                events_trigger('message_send', $eventdata);
+            }
+        }
+    /// Main savepoint reached
+        upgrade_main_savepoint($result, 2009102700);
+    }
+    
     return $result;
 }
 
Index: lib/db/upgradelib.php
=========================================================
--- lib/db/upgradelib.php	(revision 1.26)
+++ lib/db/upgradelib.php	Thu Oct 15 09:39:03 WST 2009
@@ -204,7 +204,7 @@
             }
 
             if (!$fs->file_exists(SYSCONTEXTID, 'blog', $entry->id, '/', $filename)) {
-                $file_record = array('contextid'=>SYSCONTEXTID, 'filearea'=>'blog', 'itemid'=>$entry->id, 'filepath'=>'/', 'filename'=>$filename,
+                $file_record = array('contextid'=>SYSCONTEXTID, 'filearea'=>'blog_attachment', 'itemid'=>$entry->id, 'filepath'=>'/', 'filename'=>$filename,
                                      'timecreated'=>filectime($pathname), 'timemodified'=>filemtime($pathname), 'userid'=>$entry->userid);
                 $fs->create_file_from_pathname($file_record, $pathname);
             }
Index: lib/moodlelib.php
=========================================================
--- lib/moodlelib.php	(revision 1.1255)
+++ lib/moodlelib.php	Tue Oct 20 14:43:59 WST 2009
@@ -4196,7 +4196,13 @@
         note_delete_all($data->courseid);
         $status[] = array('component'=>$componentstr, 'item'=>get_string('deletenotes', 'notes'), 'error'=>false);
     }
-
+    
+    if (!empty($data->delete_blog_associations)) {
+        require_once($CFG->dirroot.'/blog/lib.php');
+        blog_remove_associations_for_course($data->courseid);
+        $status[] = array('component'=>$componentstr, 'item'=>get_string('deleteblogassociations', 'blog'), 'error'=>false);
+    }
+
     $componentstr = get_string('roles');
 
     if (!empty($data->reset_roles_overrides)) {
Index: lib/navigationlib.php
=========================================================
--- lib/navigationlib.php	(revision 1.41)
+++ lib/navigationlib.php	Thu Oct 29 10:23:48 WST 2009
@@ -1378,7 +1378,8 @@
                         $filterselect = clean_param($filterselect, PARAM_INT);
 
                         if ($CFG->bloglevel >= 3) {
-                            $participants->add(get_string('blogs','blog'), blog_get_blogs_url(array('course'=>$filterselect))->out());
+                            $blogsurls = new moodle_url($CFG->wwwroot.'/blog/index.php', array('courseid' => $filterselect));
+                            $participants->add(get_string('blogs','blog'), $blogsurls->out());
                         }
                         
                         if (!empty($CFG->enablenotes) && (has_capability('moodle/notes:manage', $this->context) || has_capability('moodle/notes:view', $this->context))) {
Index: mod/forum/lib.php
=========================================================
--- mod/forum/lib.php	(revision 1.821)
+++ mod/forum/lib.php	Wed Oct 28 14:26:07 WST 2009
@@ -3054,6 +3054,12 @@
             $forum->assessed = 0;
             $forum->forcesubscribe = 0;
             break;
+        case "blog":
+            $forum->name = get_string('blogforum', 'forum');
+            $forum->intro = get_string('introblog', 'forum');
+            $forum->assessed = 0;
+            $forum->forcesubscribe = 0;
+            break;
         default:
             echo $OUTPUT->notification("That forum type doesn't exist!");
             return false;
@@ -5531,12 +5537,19 @@
         echo "<form id=\"newdiscussionform\" method=\"get\" action=\"$CFG->wwwroot/mod/forum/post.php\">";
         echo '<div>';
         echo "<input type=\"hidden\" name=\"forum\" value=\"$forum->id\" />";
-        echo '<input type="submit" value="';
-        echo ($forum->type == 'news') ? get_string('addanewtopic', 'forum')
-            : (($forum->type == 'qanda')
-               ? get_string('addanewquestion','forum')
-               : get_string('addanewdiscussion', 'forum'));
-        echo '" />';
+        switch ($forum->type) {
+            case 'news':
+            case 'blog':
+                $buttonadd = get_string('addanewtopic', 'forum');
+                break;
+            case 'qanda':
+                $buttonadd = get_string('addanewquestion', 'forum');
+                break;
+            default:
+                $buttonadd = get_string('addanewdiscussion', 'forum');
+                break;
+        }
+        echo '<input type="submit" value="'.$buttonadd.'" />';
         echo '</div>';
         echo '</form>';
         echo "</div>\n";
@@ -7895,7 +7908,7 @@
 }
 
 /**
- * Returns array of forum types
+ * Returns array of forum types chooseable on the forum editing form
  *
  * @return array
  */
@@ -7903,7 +7916,8 @@
     return array ('general'  => get_string('generalforum', 'forum'),
                   'eachuser' => get_string('eachuserforum', 'forum'),
                   'single'   => get_string('singleforum', 'forum'),
-                  'qanda'    => get_string('qandaforum', 'forum'));
+                  'qanda'    => get_string('qandaforum', 'forum'),
+                  'blog'     => get_string('blogforum', 'forum'));
 }
 
 /**
@@ -7917,7 +7931,8 @@
                   'general'  => get_string('generalforum', 'forum'),
                   'eachuser' => get_string('eachuserforum', 'forum'),
                   'single'   => get_string('singleforum', 'forum'),
-                  'qanda'    => get_string('qandaforum', 'forum'));
+                  'qanda'    => get_string('qandaforum', 'forum'),
+                  'blog'     => get_string('blogforum', 'forum'));
 }
 
 /**
Index: mod/forum/view.php
=========================================================
--- mod/forum/view.php	(revision 1.137)
+++ mod/forum/view.php	Thu Oct 15 09:39:03 WST 2009
@@ -179,6 +179,18 @@
             }
             break;
 
+        case 'blog':
+            if (!empty($forum->intro)) {
+                echo $OUTPUT->box(format_module_intro('forum', $forum, $cm->id), 'generalbox', 'intro');
+            }
+            echo '<br />';
+            if (!empty($showall)) {
+                forum_print_latest_discussions($course, $forum, 0, 'plain', '', -1, -1, -1, 0, $cm);
+            } else {
+                forum_print_latest_discussions($course, $forum, -1, 'plain', '', -1, -1, $page, $CFG->forum_manydiscussions, $cm);
+            }
+            break;
+
         default:
             if (!empty($forum->intro)) {
                 echo $OUTPUT->box(format_module_intro('forum', $forum, $cm->id), 'generalbox', 'intro');
Index: pluginfile.php
=========================================================
--- pluginfile.php	(revision 1.16)
+++ pluginfile.php	Thu Oct 15 09:39:03 WST 2009
@@ -57,7 +57,7 @@
 
 
 if ($context->contextlevel == CONTEXT_SYSTEM) {
-    if ($filearea === 'blog') {
+    if ($filearea === 'blog_attachment' || $filearea === 'blog_post') {
 
         if (empty($CFG->bloglevel)) {
             print_error('siteblogdisable', 'blog');
@@ -95,7 +95,7 @@
         //TODO: implement shared course and shared group access
 
         $relativepath = '/'.implode('/', $args);
-        $fullpath = $context->id.'blog'.$entryid.$relativepath;
+        $fullpath = $context->id.$filearea.$entryid.$relativepath;
 
         if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
             send_file_not_found();
Index: tag/index.php
=========================================================
--- tag/index.php	(revision 1.46)
+++ tag/index.php	Fri Oct 23 11:09:41 WST 2009
@@ -92,14 +92,15 @@
 }
 
 // Print up to 10 previous blogs entries
+if (has_capability('moodle/blog:view', $systemcontext)) {
+    require_once($CFG->dirroot.'/blog/lib.php');
+    require_once($CFG->dirroot.'/blog/locallib.php');
 
-// I was not able to use get_items_tagged_with() because it automatically
-// tries to join on 'blog' table, since the itemtype is 'blog'. However blogs
-// uses the post table so this would not really work.    - Yu 29/8/07
-if (has_capability('moodle/blog:view', $systemcontext)) {  // You have to see blogs obviously
+    $bloglisting = new blog_listing(array('tag' => $tag->id));
+    $limit = 10;
+    $start = 0;
 
-    $count = 10;
-    if ($blogs = blog_fetch_entries(array('tag'=>$tag->id), $count)) {
+    if ($blogs = $bloglisting->get_entries($start, $limit)) {
 
         echo $OUTPUT->box_start('generalbox', 'tag-blogs');
         $heading = get_string('relatedblogs', 'tag', $tagname). ' ' . get_string('taggedwith', 'tag', $tagname);
@@ -114,7 +115,7 @@
                 $class = '';
             }
             echo '<li '.$class.'>';
-            echo '<a '.$class.' href="'.$CFG->wwwroot.'/blog/index.php?postid='.$blog->id.'">';
+            echo '<a '.$class.' href="'.$CFG->wwwroot.'/blog/index.php?entryid='.$blog->id.'">';
             echo format_string($blog->subject);
             echo '</a>';
             echo ' - ';
@@ -126,7 +127,8 @@
         }
         echo '</ul>';
 
-        echo '<p class="moreblogs"><a href="'.blog_get_blogs_url(array('tag'=>$tag->id))->out().'">'.get_string('seeallblogs', 'tag', $tagname).'</a></p>';
+        $allblogsurl = new moodle_url($CFG->wwwroot.'/blog/index.php', array('tagid' => $tag->id));
+        echo '<p class="moreblogs"><a href="'.$allblogsurl->out().'">'.get_string('seeallblogs', 'tag', $tagname).'</a></p>';
 
         echo $OUTPUT->box_end();
     }
Index: theme/standard/styles_fonts.css
=========================================================
--- theme/standard/styles_fonts.css	(revision 1.183)
+++ theme/standard/styles_fonts.css	Fri Oct 23 10:01:37 WST 2009
@@ -933,7 +933,7 @@
 .forumpost .topic .subject {
   font-weight: bold;
 }
-.forumpost .topic .author {
+.forumpost .topic .author , .forumpost .topic .externalblog {
   font-size: 0.8em;
 }
 .forumpost .commands,
Index: theme/standard/styles_layout.css
=========================================================
--- theme/standard/styles_layout.css	(revision 1.722)
+++ theme/standard/styles_layout.css	Wed Oct 28 16:43:15 WST 2009
@@ -1997,6 +1997,11 @@
   width: auto;
 }
 
+.officialblogtags {
+    display: inline;
+    font-weight: bold;
+}
+
 /***
  *** Calendar
  ***/
Index: user/tabs.php
=========================================================
--- user/tabs.php	(revision 1.82)
+++ user/tabs.php	Fri Oct 23 11:46:39 WST 2009
@@ -2,7 +2,6 @@
 /// This file to be included so we can assume config.php has already been included.
 /// We also assume that $user, $course, $currenttab have been set
 
-    require_once($CFG->dirroot.'/blog/lib.php');
     require_once($CFG->libdir . '/portfoliolib.php');
 
     if (!isset($filtertype)) {
@@ -29,27 +28,17 @@
     $toprow = array();
     $systemcontext   = get_context_instance(CONTEXT_SYSTEM);
 
-    /**************************************
-     * Site Level participation or Blogs  *
-     **************************************/
+    /****************************
+     * Site Level participation *
+     ****************************/
     if ($filtertype == 'site') {
 
         $site = get_site();
         echo $OUTPUT->heading(format_string($site->fullname));
 
-        if ($CFG->bloglevel >= 4) {
-            if (has_capability('moodle/site:viewparticipants', $systemcontext)) {
-                $toprow[] = new tabobject('participants', $CFG->wwwroot.'/user/index.php?id='.SITEID,
-                    get_string('participants'));
-            }
-
-            $toprow[] = new tabobject('blogs', blog_get_blogs_url(array())->out(),
-                get_string('blogs','blog'));
-        }
-
-    /**************************************
-     * Course Level participation or Blogs  *
-     **************************************/
+    /******************************
+     * Course Level participation *
+     ******************************/
     } else if ($filtertype == 'course' && $filterselect) {
 
         $course = $DB->get_record('course', array('id'=>$filterselect));
@@ -59,34 +48,22 @@
         $toprow[] = new tabobject('participants', $CFG->wwwroot.'/user/index.php?id='.$filterselect,
             get_string('participants'));
 
-        if ($CFG->bloglevel >= 3) {
-            $toprow[] = new tabobject('blogs', blog_get_blogs_url(array('course'=>$filterselect))->out(), get_string('blogs','blog'));
-        }
-
         if (!empty($CFG->enablenotes) and (has_capability('moodle/notes:manage', $coursecontext) || has_capability('moodle/notes:view', $coursecontext))) {
             $toprow[] = new tabobject('notes', $CFG->wwwroot.'/notes/index.php?filtertype=course&amp;filterselect=' . $filterselect, get_string('notes', 'notes'));
         }
 
-    /**************************************
-     * Group Level participation or Blogs  *
-     **************************************/
+    /*****************************
+     * Group Level participation *
+     *****************************/
     } else if ($filtertype == 'group' && $filterselect) {
 
         $group_name = groups_get_group_name($filterselect);
         echo $OUTPUT->heading($group_name);
 
-        if ($CFG->bloglevel >= 2) {
 
-            $toprow[] = new tabobject('participants', $CFG->wwwroot.'/user/index.php?id='.$course->id.'&amp;group='.$filterselect,
-                get_string('participants'));
-
-
-            $toprow[] = new tabobject('blogs', blog_get_blogs_url(array('group'=>$filterselect))->out(), get_string('blogs','blog'));
-        }
-
-    /**************************************
-     * User Level participation or Blogs  *
-     **************************************/
+    /****************************
+     * User Level participation *
+     ****************************/
     } else {
         if (isset($userid)) {
             $user = $DB->get_record('user', array('id'=>$userid));
@@ -129,19 +106,6 @@
 
         }
 
-    /// Personal blog entries tab
-        require_once($CFG->dirroot.'/blog/lib.php');
-        if ($CFG->bloglevel >= BLOG_USER_LEVEL and // blogs must be enabled
-            (has_capability('moodle/user:readuserblogs', $personalcontext) // can review posts (parents etc)
-            or has_capability('moodle/blog:manageentries', $systemcontext)     // entry manager can see all posts
-            or ($user->id == $USER->id and has_capability('moodle/blog:create', $systemcontext)) // viewing self
-            or (has_capability('moodle/blog:view', $systemcontext) or has_capability('moodle/blog:view', $coursecontext))
-            ) // able to read blogs in site or course context
-        ) { //end if
-
-            $toprow[] = new tabobject('blogs', blog_get_blogs_url(array('user'=>$user->id,'course'=>$course->id))->out(), get_string('blog', 'blog'));
-        }
-
         if (!empty($CFG->enablenotes) and (has_capability('moodle/notes:manage', $coursecontext) || has_capability('moodle/notes:view', $coursecontext))) {
             $toprow[] = new tabobject('notes', $CFG->wwwroot.'/notes/index.php?course='.$course->id . '&amp;user=' . $user->id, get_string('notes', 'notes'));
         }
